From e205b25d65ccb380fa158711e24d55b6de5d9bc1 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Thu, 16 Feb 2006 19:19:58 +0000 Subject: Reorganised the source tree. We now have src/ with a couple of subdirs: * daemon/ - Contains the files specific to the polypaudio daemon. * modules/ - All loadable modules. * polyp/ - Files that are part of the public, application interface or are only used in libpolyp. * polypcore/ - All other shared files. * tests/ - Test programs. * utils/ - Utility programs. git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@487 fefdeb5f-60dc-0310-8127-8f9354f1896f --- Makefile.am | 2 +- configure.ac | 6 +- polyp/Makefile.am | 1052 ----------- polyp/alsa-util.c | 124 -- polyp/alsa-util.h | 35 - polyp/authkey-prop.c | 87 - polyp/authkey-prop.h | 43 - polyp/authkey.c | 201 --- polyp/authkey.h | 32 - polyp/autoload.c | 180 -- polyp/autoload.h | 58 - polyp/caps.c | 131 -- polyp/caps.h | 29 - polyp/cdecl.h | 42 - polyp/channelmap.c | 202 --- polyp/channelmap.h | 98 -- polyp/cli-command.c | 834 --------- polyp/cli-command.h | 40 - polyp/cli-text.c | 385 ---- polyp/cli-text.h | 42 - polyp/cli.c | 147 -- polyp/cli.h | 38 - polyp/client-conf-x11.c | 91 - polyp/client-conf-x11.h | 31 - polyp/client-conf.c | 190 -- polyp/client-conf.h | 52 - polyp/client.c | 91 - polyp/client.conf.in | 39 - polyp/client.h | 57 - polyp/cmdline.c | 300 ---- polyp/cmdline.h | 35 - polyp/conf-parser.c | 176 -- polyp/conf-parser.h | 47 - polyp/core.c | 157 -- polyp/core.h | 82 - polyp/cpulimit-test.c | 92 - polyp/cpulimit.c | 236 --- polyp/cpulimit.h | 34 - polyp/daemon-conf.c | 297 ---- polyp/daemon-conf.h | 80 - polyp/daemon.conf.in | 77 - polyp/default.pa.in | 66 - polyp/default.pa.win32 | 43 - polyp/depmod.py | 73 - polyp/dllmain.c | 46 - polyp/dumpmodules.c | 99 -- polyp/dumpmodules.h | 31 - polyp/dynarray.c | 102 -- polyp/dynarray.h | 49 - polyp/endianmacros.h | 77 - polyp/esdcompat.sh.in | 98 -- polyp/esound.h | 209 --- polyp/g711.c | 2531 --------------------------- polyp/g711.h | 40 - polyp/gccmacro.h | 53 - polyp/glib-mainloop.c | 538 ------ polyp/glib-mainloop.h | 55 - polyp/glib12-mainloop.c | 500 ------ polyp/hashmap.c | 194 -- polyp/hashmap.h | 53 - polyp/howl-wrap.c | 116 -- polyp/howl-wrap.h | 37 - polyp/idxset.c | 390 ----- polyp/idxset.h | 94 - polyp/inet_ntop.c | 80 - polyp/inet_ntop.h | 12 - polyp/iochannel.c | 292 --- polyp/iochannel.h | 72 - polyp/ioline.c | 368 ---- polyp/ioline.h | 51 - polyp/llist.h | 69 - polyp/log.c | 150 -- polyp/log.h | 69 - polyp/main.c | 472 ----- polyp/mainloop-api.c | 68 - polyp/mainloop-api.h | 120 -- polyp/mainloop-signal.c | 267 --- polyp/mainloop-signal.h | 60 - polyp/mainloop-test.c | 146 -- polyp/mainloop.c | 812 --------- polyp/mainloop.h | 90 - polyp/mcalign-test.c | 96 - polyp/mcalign.c | 188 -- polyp/mcalign.h | 77 - polyp/memblock.c | 169 -- polyp/memblock.h | 87 - polyp/memblockq.c | 343 ---- polyp/memblockq.h | 104 -- polyp/memchunk.c | 58 - polyp/memchunk.h | 45 - polyp/modargs.c | 260 --- polyp/modargs.h | 49 - polyp/modinfo.c | 84 - polyp/modinfo.h | 43 - polyp/module-alsa-sink.c | 291 --- polyp/module-alsa-source.c | 277 --- polyp/module-cli.c | 87 - polyp/module-combine.c | 393 ----- polyp/module-defs.h.m4 | 29 - polyp/module-detect.c | 226 --- polyp/module-esound-compat-spawnfd.c | 80 - polyp/module-esound-compat-spawnpid.c | 78 - polyp/module-esound-sink.c | 426 ----- polyp/module-lirc.c | 240 --- polyp/module-match.c | 234 --- polyp/module-mmkbd-evdev.c | 249 --- polyp/module-native-protocol-fd.c | 84 - polyp/module-null-sink.c | 149 -- polyp/module-oss-mmap.c | 425 ----- polyp/module-oss.c | 467 ----- polyp/module-pipe-sink.c | 236 --- polyp/module-pipe-source.c | 209 --- polyp/module-protocol-stub.c | 262 --- polyp/module-sine.c | 181 -- polyp/module-solaris.c | 486 ----- polyp/module-tunnel.c | 688 -------- polyp/module-waveout.c | 581 ------ polyp/module-x11-bell.c | 172 -- polyp/module-x11-publish.c | 191 -- polyp/module-zeroconf-publish.c | 507 ------ polyp/module.c | 275 --- polyp/module.h | 70 - polyp/namereg.c | 212 --- polyp/namereg.h | 43 - polyp/native-common.h | 114 -- polyp/oss-util.c | 167 -- polyp/oss-util.h | 32 - polyp/pabrowse.c | 139 -- polyp/pacat-simple.c | 101 -- polyp/pacat.c | 541 ------ polyp/packet.c | 69 - polyp/packet.h | 41 - polyp/pacmd.c | 183 -- polyp/pactl.c | 784 --------- polyp/paplay.c | 386 ---- polyp/parec-simple.c | 100 -- polyp/parseaddr.c | 112 -- polyp/parseaddr.h | 42 - polyp/pax11publish.c | 217 --- polyp/pdispatch.c | 295 ---- polyp/pdispatch.h | 50 - polyp/pid.c | 286 --- polyp/pid.h | 30 - polyp/play-memchunk.c | 118 -- polyp/play-memchunk.h | 36 - polyp/poll.c | 190 -- polyp/poll.h | 57 - polyp/polyplib-browser.c | 312 ---- polyp/polyplib-browser.h | 65 - polyp/polyplib-context.c | 871 --------- polyp/polyplib-context.h | 117 -- polyp/polyplib-def.h | 213 --- polyp/polyplib-error.c | 54 - polyp/polyplib-error.h | 38 - polyp/polyplib-internal.h | 154 -- polyp/polyplib-introspect.c | 1003 ----------- polyp/polyplib-introspect.h | 279 --- polyp/polyplib-operation.c | 103 -- polyp/polyplib-operation.h | 51 - polyp/polyplib-scache.c | 127 -- polyp/polyplib-scache.h | 50 - polyp/polyplib-simple.c | 393 ----- polyp/polyplib-simple.h | 80 - polyp/polyplib-stream.c | 807 --------- polyp/polyplib-stream.h | 181 -- polyp/polyplib-subscribe.c | 81 - polyp/polyplib-subscribe.h | 47 - polyp/polyplib-version.h.in | 47 - polyp/polyplib.h | 86 - polyp/props.c | 119 -- polyp/props.h | 58 - polyp/protocol-cli.c | 95 - polyp/protocol-cli.h | 35 - polyp/protocol-esound.c | 1166 ------------ polyp/protocol-esound.h | 35 - polyp/protocol-http.c | 265 --- polyp/protocol-http.h | 35 - polyp/protocol-native.c | 2216 ----------------------- polyp/protocol-native.h | 37 - polyp/protocol-simple.c | 453 ----- polyp/protocol-simple.h | 35 - polyp/pstream-util.c | 61 - polyp/pstream-util.h | 35 - polyp/pstream.c | 492 ------ polyp/pstream.h | 52 - polyp/queue.c | 108 -- polyp/queue.h | 40 - polyp/random.c | 71 - polyp/random.h | 27 - polyp/resampler.c | 614 ------- polyp/resampler.h | 73 - polyp/sample-util.c | 328 ---- polyp/sample-util.h | 53 - polyp/sample.c | 149 -- polyp/sample.h | 120 -- polyp/scache.c | 392 ----- polyp/scache.h | 62 - polyp/sconv-s16be.c | 41 - polyp/sconv-s16be.h | 28 - polyp/sconv-s16le.c | 101 -- polyp/sconv-s16le.h | 28 - polyp/sconv.c | 168 -- polyp/sconv.h | 33 - polyp/sink-input.c | 368 ---- polyp/sink-input.h | 106 -- polyp/sink.c | 445 ----- polyp/sink.h | 100 -- polyp/sioman.c | 42 - polyp/sioman.h | 28 - polyp/socket-client.c | 513 ------ polyp/socket-client.h | 47 - polyp/socket-server.c | 432 ----- polyp/socket-server.h | 45 - polyp/socket-util.c | 246 --- polyp/socket-util.h | 38 - polyp/sound-file-stream.c | 179 -- polyp/sound-file-stream.h | 29 - polyp/sound-file.c | 139 -- polyp/sound-file.h | 32 - polyp/source-output.c | 228 --- polyp/source-output.h | 92 - polyp/source.c | 195 --- polyp/source.h | 86 - polyp/strbuf.c | 166 -- polyp/strbuf.h | 38 - polyp/strlist-test.c | 42 - polyp/strlist.c | 137 -- polyp/strlist.h | 47 - polyp/subscribe.c | 230 --- polyp/subscribe.h | 37 - polyp/tagstruct.c | 609 ------- polyp/tagstruct.h | 92 - polyp/tokenizer.c | 88 - polyp/tokenizer.h | 32 - polyp/util.c | 1176 ------------- polyp/util.h | 99 -- polyp/voltest.c | 20 - polyp/volume.c | 176 -- polyp/volume.h | 107 -- polyp/winsock.h | 23 - polyp/x11prop.c | 70 - polyp/x11prop.h | 33 - polyp/x11wrap.c | 235 --- polyp/x11wrap.h | 52 - polyp/xmalloc.c | 122 -- polyp/xmalloc.h | 58 - src/Makefile.am | 1061 +++++++++++ src/client.conf.in | 39 + src/daemon.conf.in | 77 + src/daemon/caps.c | 131 ++ src/daemon/caps.h | 29 + src/daemon/cmdline.c | 300 ++++ src/daemon/cmdline.h | 35 + src/daemon/cpulimit.c | 236 +++ src/daemon/cpulimit.h | 34 + src/daemon/daemon-conf.c | 297 ++++ src/daemon/daemon-conf.h | 80 + src/daemon/dumpmodules.c | 99 ++ src/daemon/dumpmodules.h | 31 + src/daemon/main.c | 472 +++++ src/default.pa.in | 66 + src/default.pa.win32 | 43 + src/depmod.py | 73 + src/modules/module-alsa-sink.c | 292 +++ src/modules/module-alsa-source.c | 278 +++ src/modules/module-cli.c | 88 + src/modules/module-combine.c | 394 +++++ src/modules/module-defs.h.m4 | 29 + src/modules/module-detect.c | 227 +++ src/modules/module-esound-compat-spawnfd.c | 81 + src/modules/module-esound-compat-spawnpid.c | 79 + src/modules/module-esound-sink.c | 427 +++++ src/modules/module-lirc.c | 241 +++ src/modules/module-match.c | 235 +++ src/modules/module-mmkbd-evdev.c | 250 +++ src/modules/module-native-protocol-fd.c | 85 + src/modules/module-null-sink.c | 150 ++ src/modules/module-oss-mmap.c | 426 +++++ src/modules/module-oss.c | 468 +++++ src/modules/module-pipe-sink.c | 237 +++ src/modules/module-pipe-source.c | 210 +++ src/modules/module-protocol-stub.c | 261 +++ src/modules/module-sine.c | 182 ++ src/modules/module-solaris.c | 488 ++++++ src/modules/module-tunnel.c | 688 ++++++++ src/modules/module-waveout.c | 583 ++++++ src/modules/module-x11-bell.c | 173 ++ src/modules/module-x11-publish.c | 192 ++ src/modules/module-zeroconf-publish.c | 508 ++++++ src/polyp/cdecl.h | 42 + src/polyp/channelmap.c | 202 +++ src/polyp/channelmap.h | 98 ++ src/polyp/client-conf-x11.c | 91 + src/polyp/client-conf-x11.h | 31 + src/polyp/client-conf.c | 191 ++ src/polyp/client-conf.h | 52 + src/polyp/glib-mainloop.c | 538 ++++++ src/polyp/glib-mainloop.h | 55 + src/polyp/glib12-mainloop.c | 500 ++++++ src/polyp/mainloop-api.c | 68 + src/polyp/mainloop-api.h | 120 ++ src/polyp/mainloop-signal.c | 267 +++ src/polyp/mainloop-signal.h | 60 + src/polyp/mainloop.c | 812 +++++++++ src/polyp/mainloop.h | 90 + src/polyp/polyplib-browser.c | 312 ++++ src/polyp/polyplib-browser.h | 65 + src/polyp/polyplib-context.c | 871 +++++++++ src/polyp/polyplib-context.h | 117 ++ src/polyp/polyplib-def.h | 213 +++ src/polyp/polyplib-error.c | 54 + src/polyp/polyplib-error.h | 38 + src/polyp/polyplib-internal.h | 154 ++ src/polyp/polyplib-introspect.c | 1003 +++++++++++ src/polyp/polyplib-introspect.h | 279 +++ src/polyp/polyplib-operation.c | 103 ++ src/polyp/polyplib-operation.h | 51 + src/polyp/polyplib-scache.c | 127 ++ src/polyp/polyplib-scache.h | 50 + src/polyp/polyplib-simple.c | 393 +++++ src/polyp/polyplib-simple.h | 80 + src/polyp/polyplib-stream.c | 807 +++++++++ src/polyp/polyplib-stream.h | 181 ++ src/polyp/polyplib-subscribe.c | 81 + src/polyp/polyplib-subscribe.h | 47 + src/polyp/polyplib-version.h.in | 47 + src/polyp/polyplib.h | 86 + src/polyp/sample.c | 149 ++ src/polyp/sample.h | 120 ++ src/polyp/volume.c | 176 ++ src/polyp/volume.h | 107 ++ src/polypcore/alsa-util.c | 124 ++ src/polypcore/alsa-util.h | 35 + src/polypcore/authkey-prop.c | 87 + src/polypcore/authkey-prop.h | 43 + src/polypcore/authkey.c | 201 +++ src/polypcore/authkey.h | 32 + src/polypcore/autoload.c | 180 ++ src/polypcore/autoload.h | 58 + src/polypcore/cli-command.c | 834 +++++++++ src/polypcore/cli-command.h | 40 + src/polypcore/cli-text.c | 385 ++++ src/polypcore/cli-text.h | 42 + src/polypcore/cli.c | 147 ++ src/polypcore/cli.h | 38 + src/polypcore/client.c | 91 + src/polypcore/client.h | 57 + src/polypcore/conf-parser.c | 176 ++ src/polypcore/conf-parser.h | 47 + src/polypcore/core.c | 157 ++ src/polypcore/core.h | 82 + src/polypcore/dllmain.c | 46 + src/polypcore/dynarray.c | 102 ++ src/polypcore/dynarray.h | 49 + src/polypcore/endianmacros.h | 77 + src/polypcore/esound.h | 209 +++ src/polypcore/g711.c | 2531 +++++++++++++++++++++++++++ src/polypcore/g711.h | 40 + src/polypcore/gccmacro.h | 53 + src/polypcore/hashmap.c | 194 ++ src/polypcore/hashmap.h | 53 + src/polypcore/howl-wrap.c | 116 ++ src/polypcore/howl-wrap.h | 37 + src/polypcore/idxset.c | 390 +++++ src/polypcore/idxset.h | 94 + src/polypcore/inet_ntop.c | 80 + src/polypcore/inet_ntop.h | 12 + src/polypcore/iochannel.c | 292 +++ src/polypcore/iochannel.h | 72 + src/polypcore/ioline.c | 368 ++++ src/polypcore/ioline.h | 51 + src/polypcore/llist.h | 69 + src/polypcore/log.c | 150 ++ src/polypcore/log.h | 69 + src/polypcore/mcalign.c | 188 ++ src/polypcore/mcalign.h | 77 + src/polypcore/memblock.c | 169 ++ src/polypcore/memblock.h | 87 + src/polypcore/memblockq.c | 343 ++++ src/polypcore/memblockq.h | 104 ++ src/polypcore/memchunk.c | 58 + src/polypcore/memchunk.h | 45 + src/polypcore/modargs.c | 260 +++ src/polypcore/modargs.h | 49 + src/polypcore/modinfo.c | 84 + src/polypcore/modinfo.h | 43 + src/polypcore/module.c | 275 +++ src/polypcore/module.h | 70 + src/polypcore/namereg.c | 212 +++ src/polypcore/namereg.h | 43 + src/polypcore/native-common.h | 114 ++ src/polypcore/oss-util.c | 167 ++ src/polypcore/oss-util.h | 32 + src/polypcore/packet.c | 69 + src/polypcore/packet.h | 41 + src/polypcore/parseaddr.c | 112 ++ src/polypcore/parseaddr.h | 42 + src/polypcore/pdispatch.c | 295 ++++ src/polypcore/pdispatch.h | 50 + src/polypcore/pid.c | 286 +++ src/polypcore/pid.h | 30 + src/polypcore/play-memchunk.c | 118 ++ src/polypcore/play-memchunk.h | 36 + src/polypcore/poll.c | 190 ++ src/polypcore/poll.h | 57 + src/polypcore/props.c | 119 ++ src/polypcore/props.h | 58 + src/polypcore/protocol-cli.c | 95 + src/polypcore/protocol-cli.h | 35 + src/polypcore/protocol-esound.c | 1166 ++++++++++++ src/polypcore/protocol-esound.h | 35 + src/polypcore/protocol-http.c | 265 +++ src/polypcore/protocol-http.h | 35 + src/polypcore/protocol-native.c | 2216 +++++++++++++++++++++++ src/polypcore/protocol-native.h | 37 + src/polypcore/protocol-simple.c | 453 +++++ src/polypcore/protocol-simple.h | 35 + src/polypcore/pstream-util.c | 61 + src/polypcore/pstream-util.h | 35 + src/polypcore/pstream.c | 492 ++++++ src/polypcore/pstream.h | 52 + src/polypcore/queue.c | 108 ++ src/polypcore/queue.h | 40 + src/polypcore/random.c | 71 + src/polypcore/random.h | 27 + src/polypcore/resampler.c | 614 +++++++ src/polypcore/resampler.h | 73 + src/polypcore/sample-util.c | 328 ++++ src/polypcore/sample-util.h | 53 + src/polypcore/scache.c | 392 +++++ src/polypcore/scache.h | 62 + src/polypcore/sconv-s16be.c | 41 + src/polypcore/sconv-s16be.h | 28 + src/polypcore/sconv-s16le.c | 101 ++ src/polypcore/sconv-s16le.h | 28 + src/polypcore/sconv.c | 168 ++ src/polypcore/sconv.h | 33 + src/polypcore/sink-input.c | 368 ++++ src/polypcore/sink-input.h | 106 ++ src/polypcore/sink.c | 445 +++++ src/polypcore/sink.h | 100 ++ src/polypcore/sioman.c | 42 + src/polypcore/sioman.h | 28 + src/polypcore/socket-client.c | 513 ++++++ src/polypcore/socket-client.h | 47 + src/polypcore/socket-server.c | 432 +++++ src/polypcore/socket-server.h | 45 + src/polypcore/socket-util.c | 246 +++ src/polypcore/socket-util.h | 38 + src/polypcore/sound-file-stream.c | 179 ++ src/polypcore/sound-file-stream.h | 29 + src/polypcore/sound-file.c | 139 ++ src/polypcore/sound-file.h | 32 + src/polypcore/source-output.c | 228 +++ src/polypcore/source-output.h | 92 + src/polypcore/source.c | 195 +++ src/polypcore/source.h | 86 + src/polypcore/strbuf.c | 167 ++ src/polypcore/strbuf.h | 38 + src/polypcore/strlist.c | 137 ++ src/polypcore/strlist.h | 47 + src/polypcore/subscribe.c | 230 +++ src/polypcore/subscribe.h | 37 + src/polypcore/tagstruct.c | 609 +++++++ src/polypcore/tagstruct.h | 92 + src/polypcore/tokenizer.c | 88 + src/polypcore/tokenizer.h | 32 + src/polypcore/util.c | 1176 +++++++++++++ src/polypcore/util.h | 99 ++ src/polypcore/winsock.h | 23 + src/polypcore/x11prop.c | 70 + src/polypcore/x11prop.h | 33 + src/polypcore/x11wrap.c | 235 +++ src/polypcore/x11wrap.h | 52 + src/polypcore/xmalloc.c | 122 ++ src/polypcore/xmalloc.h | 58 + src/tests/cpulimit-test.c | 92 + src/tests/mainloop-test.c | 146 ++ src/tests/mcalign-test.c | 96 + src/tests/pacat-simple.c | 101 ++ src/tests/parec-simple.c | 100 ++ src/tests/strlist-test.c | 42 + src/tests/voltest.c | 20 + src/utils/esdcompat.sh.in | 98 ++ src/utils/pabrowse.c | 139 ++ src/utils/pacat.c | 541 ++++++ src/utils/pacmd.c | 183 ++ src/utils/pactl.c | 784 +++++++++ src/utils/paplay.c | 386 ++++ src/utils/pax11publish.c | 217 +++ 490 files changed, 48099 insertions(+), 48064 deletions(-) delete mode 100644 polyp/Makefile.am delete mode 100644 polyp/alsa-util.c delete mode 100644 polyp/alsa-util.h delete mode 100644 polyp/authkey-prop.c delete mode 100644 polyp/authkey-prop.h delete mode 100644 polyp/authkey.c delete mode 100644 polyp/authkey.h delete mode 100644 polyp/autoload.c delete mode 100644 polyp/autoload.h delete mode 100644 polyp/caps.c delete mode 100644 polyp/caps.h delete mode 100644 polyp/cdecl.h delete mode 100644 polyp/channelmap.c delete mode 100644 polyp/channelmap.h delete mode 100644 polyp/cli-command.c delete mode 100644 polyp/cli-command.h delete mode 100644 polyp/cli-text.c delete mode 100644 polyp/cli-text.h delete mode 100644 polyp/cli.c delete mode 100644 polyp/cli.h delete mode 100644 polyp/client-conf-x11.c delete mode 100644 polyp/client-conf-x11.h delete mode 100644 polyp/client-conf.c delete mode 100644 polyp/client-conf.h delete mode 100644 polyp/client.c delete mode 100644 polyp/client.conf.in delete mode 100644 polyp/client.h delete mode 100644 polyp/cmdline.c delete mode 100644 polyp/cmdline.h delete mode 100644 polyp/conf-parser.c delete mode 100644 polyp/conf-parser.h delete mode 100644 polyp/core.c delete mode 100644 polyp/core.h delete mode 100644 polyp/cpulimit-test.c delete mode 100644 polyp/cpulimit.c delete mode 100644 polyp/cpulimit.h delete mode 100644 polyp/daemon-conf.c delete mode 100644 polyp/daemon-conf.h delete mode 100644 polyp/daemon.conf.in delete mode 100755 polyp/default.pa.in delete mode 100644 polyp/default.pa.win32 delete mode 100755 polyp/depmod.py delete mode 100644 polyp/dllmain.c delete mode 100644 polyp/dumpmodules.c delete mode 100644 polyp/dumpmodules.h delete mode 100644 polyp/dynarray.c delete mode 100644 polyp/dynarray.h delete mode 100644 polyp/endianmacros.h delete mode 100755 polyp/esdcompat.sh.in delete mode 100644 polyp/esound.h delete mode 100644 polyp/g711.c delete mode 100644 polyp/g711.h delete mode 100644 polyp/gccmacro.h delete mode 100644 polyp/glib-mainloop.c delete mode 100644 polyp/glib-mainloop.h delete mode 100644 polyp/glib12-mainloop.c delete mode 100644 polyp/hashmap.c delete mode 100644 polyp/hashmap.h delete mode 100644 polyp/howl-wrap.c delete mode 100644 polyp/howl-wrap.h delete mode 100644 polyp/idxset.c delete mode 100644 polyp/idxset.h delete mode 100644 polyp/inet_ntop.c delete mode 100644 polyp/inet_ntop.h delete mode 100644 polyp/iochannel.c delete mode 100644 polyp/iochannel.h delete mode 100644 polyp/ioline.c delete mode 100644 polyp/ioline.h delete mode 100644 polyp/llist.h delete mode 100644 polyp/log.c delete mode 100644 polyp/log.h delete mode 100644 polyp/main.c delete mode 100644 polyp/mainloop-api.c delete mode 100644 polyp/mainloop-api.h delete mode 100644 polyp/mainloop-signal.c delete mode 100644 polyp/mainloop-signal.h delete mode 100644 polyp/mainloop-test.c delete mode 100644 polyp/mainloop.c delete mode 100644 polyp/mainloop.h delete mode 100644 polyp/mcalign-test.c delete mode 100644 polyp/mcalign.c delete mode 100644 polyp/mcalign.h delete mode 100644 polyp/memblock.c delete mode 100644 polyp/memblock.h delete mode 100644 polyp/memblockq.c delete mode 100644 polyp/memblockq.h delete mode 100644 polyp/memchunk.c delete mode 100644 polyp/memchunk.h delete mode 100644 polyp/modargs.c delete mode 100644 polyp/modargs.h delete mode 100644 polyp/modinfo.c delete mode 100644 polyp/modinfo.h delete mode 100644 polyp/module-alsa-sink.c delete mode 100644 polyp/module-alsa-source.c delete mode 100644 polyp/module-cli.c delete mode 100644 polyp/module-combine.c delete mode 100644 polyp/module-defs.h.m4 delete mode 100644 polyp/module-detect.c delete mode 100644 polyp/module-esound-compat-spawnfd.c delete mode 100644 polyp/module-esound-compat-spawnpid.c delete mode 100644 polyp/module-esound-sink.c delete mode 100644 polyp/module-lirc.c delete mode 100644 polyp/module-match.c delete mode 100644 polyp/module-mmkbd-evdev.c delete mode 100644 polyp/module-native-protocol-fd.c delete mode 100644 polyp/module-null-sink.c delete mode 100644 polyp/module-oss-mmap.c delete mode 100644 polyp/module-oss.c delete mode 100644 polyp/module-pipe-sink.c delete mode 100644 polyp/module-pipe-source.c delete mode 100644 polyp/module-protocol-stub.c delete mode 100644 polyp/module-sine.c delete mode 100644 polyp/module-solaris.c delete mode 100644 polyp/module-tunnel.c delete mode 100644 polyp/module-waveout.c delete mode 100644 polyp/module-x11-bell.c delete mode 100644 polyp/module-x11-publish.c delete mode 100644 polyp/module-zeroconf-publish.c delete mode 100644 polyp/module.c delete mode 100644 polyp/module.h delete mode 100644 polyp/namereg.c delete mode 100644 polyp/namereg.h delete mode 100644 polyp/native-common.h delete mode 100644 polyp/oss-util.c delete mode 100644 polyp/oss-util.h delete mode 100644 polyp/pabrowse.c delete mode 100644 polyp/pacat-simple.c delete mode 100644 polyp/pacat.c delete mode 100644 polyp/packet.c delete mode 100644 polyp/packet.h delete mode 100644 polyp/pacmd.c delete mode 100644 polyp/pactl.c delete mode 100644 polyp/paplay.c delete mode 100644 polyp/parec-simple.c delete mode 100644 polyp/parseaddr.c delete mode 100644 polyp/parseaddr.h delete mode 100644 polyp/pax11publish.c delete mode 100644 polyp/pdispatch.c delete mode 100644 polyp/pdispatch.h delete mode 100644 polyp/pid.c delete mode 100644 polyp/pid.h delete mode 100644 polyp/play-memchunk.c delete mode 100644 polyp/play-memchunk.h delete mode 100644 polyp/poll.c delete mode 100644 polyp/poll.h delete mode 100644 polyp/polyplib-browser.c delete mode 100644 polyp/polyplib-browser.h delete mode 100644 polyp/polyplib-context.c delete mode 100644 polyp/polyplib-context.h delete mode 100644 polyp/polyplib-def.h delete mode 100644 polyp/polyplib-error.c delete mode 100644 polyp/polyplib-error.h delete mode 100644 polyp/polyplib-internal.h delete mode 100644 polyp/polyplib-introspect.c delete mode 100644 polyp/polyplib-introspect.h delete mode 100644 polyp/polyplib-operation.c delete mode 100644 polyp/polyplib-operation.h delete mode 100644 polyp/polyplib-scache.c delete mode 100644 polyp/polyplib-scache.h delete mode 100644 polyp/polyplib-simple.c delete mode 100644 polyp/polyplib-simple.h delete mode 100644 polyp/polyplib-stream.c delete mode 100644 polyp/polyplib-stream.h delete mode 100644 polyp/polyplib-subscribe.c delete mode 100644 polyp/polyplib-subscribe.h delete mode 100644 polyp/polyplib-version.h.in delete mode 100644 polyp/polyplib.h delete mode 100644 polyp/props.c delete mode 100644 polyp/props.h delete mode 100644 polyp/protocol-cli.c delete mode 100644 polyp/protocol-cli.h delete mode 100644 polyp/protocol-esound.c delete mode 100644 polyp/protocol-esound.h delete mode 100644 polyp/protocol-http.c delete mode 100644 polyp/protocol-http.h delete mode 100644 polyp/protocol-native.c delete mode 100644 polyp/protocol-native.h delete mode 100644 polyp/protocol-simple.c delete mode 100644 polyp/protocol-simple.h delete mode 100644 polyp/pstream-util.c delete mode 100644 polyp/pstream-util.h delete mode 100644 polyp/pstream.c delete mode 100644 polyp/pstream.h delete mode 100644 polyp/queue.c delete mode 100644 polyp/queue.h delete mode 100644 polyp/random.c delete mode 100644 polyp/random.h delete mode 100644 polyp/resampler.c delete mode 100644 polyp/resampler.h delete mode 100644 polyp/sample-util.c delete mode 100644 polyp/sample-util.h delete mode 100644 polyp/sample.c delete mode 100644 polyp/sample.h delete mode 100644 polyp/scache.c delete mode 100644 polyp/scache.h delete mode 100644 polyp/sconv-s16be.c delete mode 100644 polyp/sconv-s16be.h delete mode 100644 polyp/sconv-s16le.c delete mode 100644 polyp/sconv-s16le.h delete mode 100644 polyp/sconv.c delete mode 100644 polyp/sconv.h delete mode 100644 polyp/sink-input.c delete mode 100644 polyp/sink-input.h delete mode 100644 polyp/sink.c delete mode 100644 polyp/sink.h delete mode 100644 polyp/sioman.c delete mode 100644 polyp/sioman.h delete mode 100644 polyp/socket-client.c delete mode 100644 polyp/socket-client.h delete mode 100644 polyp/socket-server.c delete mode 100644 polyp/socket-server.h delete mode 100644 polyp/socket-util.c delete mode 100644 polyp/socket-util.h delete mode 100644 polyp/sound-file-stream.c delete mode 100644 polyp/sound-file-stream.h delete mode 100644 polyp/sound-file.c delete mode 100644 polyp/sound-file.h delete mode 100644 polyp/source-output.c delete mode 100644 polyp/source-output.h delete mode 100644 polyp/source.c delete mode 100644 polyp/source.h delete mode 100644 polyp/strbuf.c delete mode 100644 polyp/strbuf.h delete mode 100644 polyp/strlist-test.c delete mode 100644 polyp/strlist.c delete mode 100644 polyp/strlist.h delete mode 100644 polyp/subscribe.c delete mode 100644 polyp/subscribe.h delete mode 100644 polyp/tagstruct.c delete mode 100644 polyp/tagstruct.h delete mode 100644 polyp/tokenizer.c delete mode 100644 polyp/tokenizer.h delete mode 100644 polyp/util.c delete mode 100644 polyp/util.h delete mode 100644 polyp/voltest.c delete mode 100644 polyp/volume.c delete mode 100644 polyp/volume.h delete mode 100644 polyp/winsock.h delete mode 100644 polyp/x11prop.c delete mode 100644 polyp/x11prop.h delete mode 100644 polyp/x11wrap.c delete mode 100644 polyp/x11wrap.h delete mode 100644 polyp/xmalloc.c delete mode 100644 polyp/xmalloc.h create mode 100644 src/Makefile.am create mode 100644 src/client.conf.in create mode 100644 src/daemon.conf.in create mode 100644 src/daemon/caps.c create mode 100644 src/daemon/caps.h create mode 100644 src/daemon/cmdline.c create mode 100644 src/daemon/cmdline.h create mode 100644 src/daemon/cpulimit.c create mode 100644 src/daemon/cpulimit.h create mode 100644 src/daemon/daemon-conf.c create mode 100644 src/daemon/daemon-conf.h create mode 100644 src/daemon/dumpmodules.c create mode 100644 src/daemon/dumpmodules.h create mode 100644 src/daemon/main.c create mode 100755 src/default.pa.in create mode 100644 src/default.pa.win32 create mode 100755 src/depmod.py create mode 100644 src/modules/module-alsa-sink.c create mode 100644 src/modules/module-alsa-source.c create mode 100644 src/modules/module-cli.c create mode 100644 src/modules/module-combine.c create mode 100644 src/modules/module-defs.h.m4 create mode 100644 src/modules/module-detect.c create mode 100644 src/modules/module-esound-compat-spawnfd.c create mode 100644 src/modules/module-esound-compat-spawnpid.c create mode 100644 src/modules/module-esound-sink.c create mode 100644 src/modules/module-lirc.c create mode 100644 src/modules/module-match.c create mode 100644 src/modules/module-mmkbd-evdev.c create mode 100644 src/modules/module-native-protocol-fd.c create mode 100644 src/modules/module-null-sink.c create mode 100644 src/modules/module-oss-mmap.c create mode 100644 src/modules/module-oss.c create mode 100644 src/modules/module-pipe-sink.c create mode 100644 src/modules/module-pipe-source.c create mode 100644 src/modules/module-protocol-stub.c create mode 100644 src/modules/module-sine.c create mode 100644 src/modules/module-solaris.c create mode 100644 src/modules/module-tunnel.c create mode 100644 src/modules/module-waveout.c create mode 100644 src/modules/module-x11-bell.c create mode 100644 src/modules/module-x11-publish.c create mode 100644 src/modules/module-zeroconf-publish.c create mode 100644 src/polyp/cdecl.h create mode 100644 src/polyp/channelmap.c create mode 100644 src/polyp/channelmap.h create mode 100644 src/polyp/client-conf-x11.c create mode 100644 src/polyp/client-conf-x11.h create mode 100644 src/polyp/client-conf.c create mode 100644 src/polyp/client-conf.h create mode 100644 src/polyp/glib-mainloop.c create mode 100644 src/polyp/glib-mainloop.h create mode 100644 src/polyp/glib12-mainloop.c create mode 100644 src/polyp/mainloop-api.c create mode 100644 src/polyp/mainloop-api.h create mode 100644 src/polyp/mainloop-signal.c create mode 100644 src/polyp/mainloop-signal.h create mode 100644 src/polyp/mainloop.c create mode 100644 src/polyp/mainloop.h create mode 100644 src/polyp/polyplib-browser.c create mode 100644 src/polyp/polyplib-browser.h create mode 100644 src/polyp/polyplib-context.c create mode 100644 src/polyp/polyplib-context.h create mode 100644 src/polyp/polyplib-def.h create mode 100644 src/polyp/polyplib-error.c create mode 100644 src/polyp/polyplib-error.h create mode 100644 src/polyp/polyplib-internal.h create mode 100644 src/polyp/polyplib-introspect.c create mode 100644 src/polyp/polyplib-introspect.h create mode 100644 src/polyp/polyplib-operation.c create mode 100644 src/polyp/polyplib-operation.h create mode 100644 src/polyp/polyplib-scache.c create mode 100644 src/polyp/polyplib-scache.h create mode 100644 src/polyp/polyplib-simple.c create mode 100644 src/polyp/polyplib-simple.h create mode 100644 src/polyp/polyplib-stream.c create mode 100644 src/polyp/polyplib-stream.h create mode 100644 src/polyp/polyplib-subscribe.c create mode 100644 src/polyp/polyplib-subscribe.h create mode 100644 src/polyp/polyplib-version.h.in create mode 100644 src/polyp/polyplib.h create mode 100644 src/polyp/sample.c create mode 100644 src/polyp/sample.h create mode 100644 src/polyp/volume.c create mode 100644 src/polyp/volume.h create mode 100644 src/polypcore/alsa-util.c create mode 100644 src/polypcore/alsa-util.h create mode 100644 src/polypcore/authkey-prop.c create mode 100644 src/polypcore/authkey-prop.h create mode 100644 src/polypcore/authkey.c create mode 100644 src/polypcore/authkey.h create mode 100644 src/polypcore/autoload.c create mode 100644 src/polypcore/autoload.h create mode 100644 src/polypcore/cli-command.c create mode 100644 src/polypcore/cli-command.h create mode 100644 src/polypcore/cli-text.c create mode 100644 src/polypcore/cli-text.h create mode 100644 src/polypcore/cli.c create mode 100644 src/polypcore/cli.h create mode 100644 src/polypcore/client.c create mode 100644 src/polypcore/client.h create mode 100644 src/polypcore/conf-parser.c create mode 100644 src/polypcore/conf-parser.h create mode 100644 src/polypcore/core.c create mode 100644 src/polypcore/core.h create mode 100644 src/polypcore/dllmain.c create mode 100644 src/polypcore/dynarray.c create mode 100644 src/polypcore/dynarray.h create mode 100644 src/polypcore/endianmacros.h create mode 100644 src/polypcore/esound.h create mode 100644 src/polypcore/g711.c create mode 100644 src/polypcore/g711.h create mode 100644 src/polypcore/gccmacro.h create mode 100644 src/polypcore/hashmap.c create mode 100644 src/polypcore/hashmap.h create mode 100644 src/polypcore/howl-wrap.c create mode 100644 src/polypcore/howl-wrap.h create mode 100644 src/polypcore/idxset.c create mode 100644 src/polypcore/idxset.h create mode 100644 src/polypcore/inet_ntop.c create mode 100644 src/polypcore/inet_ntop.h create mode 100644 src/polypcore/iochannel.c create mode 100644 src/polypcore/iochannel.h create mode 100644 src/polypcore/ioline.c create mode 100644 src/polypcore/ioline.h create mode 100644 src/polypcore/llist.h create mode 100644 src/polypcore/log.c create mode 100644 src/polypcore/log.h create mode 100644 src/polypcore/mcalign.c create mode 100644 src/polypcore/mcalign.h create mode 100644 src/polypcore/memblock.c create mode 100644 src/polypcore/memblock.h create mode 100644 src/polypcore/memblockq.c create mode 100644 src/polypcore/memblockq.h create mode 100644 src/polypcore/memchunk.c create mode 100644 src/polypcore/memchunk.h create mode 100644 src/polypcore/modargs.c create mode 100644 src/polypcore/modargs.h create mode 100644 src/polypcore/modinfo.c create mode 100644 src/polypcore/modinfo.h create mode 100644 src/polypcore/module.c create mode 100644 src/polypcore/module.h create mode 100644 src/polypcore/namereg.c create mode 100644 src/polypcore/namereg.h create mode 100644 src/polypcore/native-common.h create mode 100644 src/polypcore/oss-util.c create mode 100644 src/polypcore/oss-util.h create mode 100644 src/polypcore/packet.c create mode 100644 src/polypcore/packet.h create mode 100644 src/polypcore/parseaddr.c create mode 100644 src/polypcore/parseaddr.h create mode 100644 src/polypcore/pdispatch.c create mode 100644 src/polypcore/pdispatch.h create mode 100644 src/polypcore/pid.c create mode 100644 src/polypcore/pid.h create mode 100644 src/polypcore/play-memchunk.c create mode 100644 src/polypcore/play-memchunk.h create mode 100644 src/polypcore/poll.c create mode 100644 src/polypcore/poll.h create mode 100644 src/polypcore/props.c create mode 100644 src/polypcore/props.h create mode 100644 src/polypcore/protocol-cli.c create mode 100644 src/polypcore/protocol-cli.h create mode 100644 src/polypcore/protocol-esound.c create mode 100644 src/polypcore/protocol-esound.h create mode 100644 src/polypcore/protocol-http.c create mode 100644 src/polypcore/protocol-http.h create mode 100644 src/polypcore/protocol-native.c create mode 100644 src/polypcore/protocol-native.h create mode 100644 src/polypcore/protocol-simple.c create mode 100644 src/polypcore/protocol-simple.h create mode 100644 src/polypcore/pstream-util.c create mode 100644 src/polypcore/pstream-util.h create mode 100644 src/polypcore/pstream.c create mode 100644 src/polypcore/pstream.h create mode 100644 src/polypcore/queue.c create mode 100644 src/polypcore/queue.h create mode 100644 src/polypcore/random.c create mode 100644 src/polypcore/random.h create mode 100644 src/polypcore/resampler.c create mode 100644 src/polypcore/resampler.h create mode 100644 src/polypcore/sample-util.c create mode 100644 src/polypcore/sample-util.h create mode 100644 src/polypcore/scache.c create mode 100644 src/polypcore/scache.h create mode 100644 src/polypcore/sconv-s16be.c create mode 100644 src/polypcore/sconv-s16be.h create mode 100644 src/polypcore/sconv-s16le.c create mode 100644 src/polypcore/sconv-s16le.h create mode 100644 src/polypcore/sconv.c create mode 100644 src/polypcore/sconv.h create mode 100644 src/polypcore/sink-input.c create mode 100644 src/polypcore/sink-input.h create mode 100644 src/polypcore/sink.c create mode 100644 src/polypcore/sink.h create mode 100644 src/polypcore/sioman.c create mode 100644 src/polypcore/sioman.h create mode 100644 src/polypcore/socket-client.c create mode 100644 src/polypcore/socket-client.h create mode 100644 src/polypcore/socket-server.c create mode 100644 src/polypcore/socket-server.h create mode 100644 src/polypcore/socket-util.c create mode 100644 src/polypcore/socket-util.h create mode 100644 src/polypcore/sound-file-stream.c create mode 100644 src/polypcore/sound-file-stream.h create mode 100644 src/polypcore/sound-file.c create mode 100644 src/polypcore/sound-file.h create mode 100644 src/polypcore/source-output.c create mode 100644 src/polypcore/source-output.h create mode 100644 src/polypcore/source.c create mode 100644 src/polypcore/source.h create mode 100644 src/polypcore/strbuf.c create mode 100644 src/polypcore/strbuf.h create mode 100644 src/polypcore/strlist.c create mode 100644 src/polypcore/strlist.h create mode 100644 src/polypcore/subscribe.c create mode 100644 src/polypcore/subscribe.h create mode 100644 src/polypcore/tagstruct.c create mode 100644 src/polypcore/tagstruct.h create mode 100644 src/polypcore/tokenizer.c create mode 100644 src/polypcore/tokenizer.h create mode 100644 src/polypcore/util.c create mode 100644 src/polypcore/util.h create mode 100644 src/polypcore/winsock.h create mode 100644 src/polypcore/x11prop.c create mode 100644 src/polypcore/x11prop.h create mode 100644 src/polypcore/x11wrap.c create mode 100644 src/polypcore/x11wrap.h create mode 100644 src/polypcore/xmalloc.c create mode 100644 src/polypcore/xmalloc.h create mode 100644 src/tests/cpulimit-test.c create mode 100644 src/tests/mainloop-test.c create mode 100644 src/tests/mcalign-test.c create mode 100644 src/tests/pacat-simple.c create mode 100644 src/tests/parec-simple.c create mode 100644 src/tests/strlist-test.c create mode 100644 src/tests/voltest.c create mode 100755 src/utils/esdcompat.sh.in create mode 100644 src/utils/pabrowse.c create mode 100644 src/utils/pacat.c create mode 100644 src/utils/pacmd.c create mode 100644 src/utils/pactl.c create mode 100644 src/utils/paplay.c create mode 100644 src/utils/pax11publish.c diff --git a/Makefile.am b/Makefile.am index 2cf93c75..9705d45d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -18,7 +18,7 @@ # USA. EXTRA_DIST = bootstrap.sh LICENSE doxygen/Makefile.am doxygen/Makefile.in doxygen/doxygen.conf.in libtool.m4 ltdl.m4 -SUBDIRS=libltdl polyp doc +SUBDIRS=libltdl src doc MAINTAINERCLEANFILES = noinst_DATA = diff --git a/configure.ac b/configure.ac index 03663590..182a2455 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ AC_PREREQ(2.57) AC_INIT([polypaudio],[0.8],[mzcbylcnhqvb (at) 0pointer (dot) de]) -AC_CONFIG_SRCDIR([polyp/main.c]) +AC_CONFIG_SRCDIR([src/daemon/main.c]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([foreign -Wall]) @@ -392,7 +392,7 @@ fi AC_CONFIG_FILES([ Makefile -polyp/Makefile +src/Makefile polyplib.pc polyplib-simple.pc polyplib-mainloop.pc @@ -407,7 +407,7 @@ doc/daemon.html doc/modules.html doxygen/Makefile doxygen/doxygen.conf -polyp/polyplib-version.h +src/polyp/polyplib-version.h doc/FAQ.html ]) AC_OUTPUT diff --git a/polyp/Makefile.am b/polyp/Makefile.am deleted file mode 100644 index 061b82b4..00000000 --- a/polyp/Makefile.am +++ /dev/null @@ -1,1052 +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. - - -################################### -# Extra directories # -################################### - -polypincludedir=$(includedir)/polyp -polypconfdir=$(sysconfdir)/polypaudio - -modlibdir=$(libdir)/polypaudio - -################################### -# Defines # -################################### - -POLYPAUDIO_BINARY=$(bindir)/polypaudio$(EXEEXT) -if OS_IS_WIN32 -DEFAULT_CONFIG_DIR=%POLYP_ROOT% -else -DEFAULT_CONFIG_DIR=$(polypconfdir) -endif - -################################### -# Compiler/linker flags # -################################### - -AM_CFLAGS = -I$(top_srcdir) -AM_CFLAGS += $(PTHREAD_CFLAGS) -D_POSIX_PTHREAD_SEMANTICS -AM_CFLAGS += $(LTDLINCL) -AM_CFLAGS += $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) -AM_CFLAGS += -DDLSEARCHPATH=\"$(modlibdir)\" -#AM_CFLAGS += -DDLSEARCHPATH=\"$(shell pwd)\" -AM_CFLAGS += -DDEFAULT_CONFIG_DIR=\"$(DEFAULT_CONFIG_DIR)\" -AM_CFLAGS += -DPOLYPAUDIO_BINARY=\"$(POLYPAUDIO_BINARY)\" - -# This cool debug trap works on i386/gcc only -AM_CFLAGS += '-DDEBUG_TRAP=__asm__("int $$3")' - -AM_LIBADD = $(PTHREAD_LIBS) -AM_LDADD = $(PTHREAD_LIBS) - -# Only required on some platforms but defined for all to avoid errors -AM_LDFLAGS = -no-undefined - -if STATIC_BINS -BINLDFLAGS = -static -endif - -if OS_IS_WIN32 -AM_LDFLAGS+=-Wl,--export-all-symbols -WINSOCK_LIBS=-lwsock32 -lws2_32 -lwininet -endif - -################################### -# Extra files # -################################### - -EXTRA_DIST = \ - client.conf.in \ - daemon.conf.in \ - default.pa.in \ - depmod.py \ - esdcompat.sh.in \ - module-defs.h.m4 - -polypconf_DATA = default.pa daemon.conf client.conf - -BUILT_SOURCES = polyplib-version.h - -################################### -# Main daemon # -################################### - -bin_PROGRAMS = polypaudio - -polypaudio_SOURCES = \ - caps.h caps.c \ - cmdline.c cmdline.h \ - cpulimit.c cpulimit.h \ - conf-parser.h conf-parser.c \ - daemon-conf.c daemon-conf.h \ - dumpmodules.c dumpmodules.h \ - gcc-printf.h \ - main.c \ - pid.c pid.h - -polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) -polypaudio_CPPFLAGS = $(AM_CPPFLAGS) -polypaudio_LDADD = $(AM_LDADD) libpolypcore.la $(LIBLTDL) \ - $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS) -polypaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -dlopen force $(foreach f,$(PREOPEN_LIBS),-dlopen $(f)) - -if PREOPEN_MODS -PREOPEN_LIBS = $(PREOPEN_MODS) -else -PREOPEN_LIBS = $(modlib_LTLIBRARIES) -endif - -################################### -# Utility programs # -################################### - -bin_PROGRAMS += \ - pacat \ - pactl \ - paplay - -if HAVE_AF_UNIX -bin_PROGRAMS += pacmd -endif - -if HAVE_X11 -bin_PROGRAMS += pax11publish -endif - -if HAVE_HOWL -bin_PROGRAMS += pabrowse -endif - -bin_SCRIPTS = esdcompat.sh - -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) -pacat_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - -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) -paplay_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - -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) -pactl_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - -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) -pacmd_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - -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) -pax11publish_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - -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) -pabrowse_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - -################################### -# Test programs # -################################### - -noinst_PROGRAMS = \ - mainloop-test \ - mcalign-test \ - pacat-simple \ - parec-simple \ - strlist-test \ - voltest - -if HAVE_SIGXCPU -noinst_PROGRAMS += \ - cpulimit-test \ - cpulimit-test2 -endif - -if HAVE_GLIB20 -noinst_PROGRAMS += \ - mainloop-test-glib -endif - -if HAVE_GLIB12 -noinst_PROGRAMS += \ - mainloop-test-glib12 -endif - -mainloop_test_SOURCES = mainloop-test.c -mainloop_test_CFLAGS = $(AM_CFLAGS) -mainloop_test_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la libpolyp-@PA_MAJORMINOR@.la -mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - -mcalign_test_SOURCES = 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) $(WINSOCK_LIBS) -mcalign_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - -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) -pacat_simple_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - -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) -parec_simple_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - -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) $(WINSOCK_LIBS) -strlist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - -voltest_SOURCES = voltest.c sample.c volume.c volume.h sample.h -voltest_CFLAGS = $(AM_CFLAGS) -voltest_LDADD = $(AM_LDADD) -voltest_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - -cpulimit_test_SOURCES = cpulimit-test.c cpulimit.c util.c log.c cpulimit.h util.h log.h idxset.c idxset.h -cpulimit_test_CFLAGS = $(AM_CFLAGS) -cpulimit_test_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la -cpulimit_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - -cpulimit_test2_SOURCES = cpulimit-test.c cpulimit.c util.c log.c cpulimit.h util.h log.h idxset.c idxset.h -cpulimit_test2_CFLAGS = $(AM_CFLAGS) -DTEST2 -cpulimit_test2_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la -cpulimit_test2_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - -mainloop_test_glib_SOURCES = $(mainloop_test_SOURCES) -mainloop_test_glib_CFLAGS = $(mainloop_test_CFLAGS) $(GLIB20_CFLAGS) -DGLIB_MAIN_LOOP -mainloop_test_glib_LDADD = $(mainloop_test_LDADD) $(GLIB20_LIBS) libpolyp-mainloop-glib-@PA_MAJORMINOR@.la -mainloop_test_glib_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - -mainloop_test_glib12_SOURCES = $(mainloop_test_SOURCES) -mainloop_test_glib12_CFLAGS = $(mainloop_test_CFLAGS) $(GLIB12_CFLAGS) -DGLIB_MAIN_LOOP -mainloop_test_glib12_LDADD = $(mainloop_test_LDADD) $(GLIB12_LIBS) libpolyp-mainloop-glib12-@PA_MAJORMINOR@.la -mainloop_test_glib12_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) - -################################### -# Client library # -################################### - -polypinclude_HEADERS = \ - cdecl.h \ - channelmap.h \ - glib-mainloop.h \ - mainloop.h \ - mainloop-api.h \ - mainloop-signal.h \ - polyplib.h \ - polyplib-context.h \ - polyplib-def.h \ - polyplib-error.h \ - polyplib-introspect.h \ - polyplib-operation.h \ - polyplib-scache.h \ - polyplib-simple.h \ - polyplib-stream.h \ - polyplib-subscribe.h \ - polyplib-version.h \ - sample.h \ - volume.h - -if HAVE_HOWL -polypinclude_HEADERS += \ - polyplib-browser.h -endif - -lib_LTLIBRARIES = \ - libpolyp-@PA_MAJORMINOR@.la \ - libpolyp-error-@PA_MAJORMINOR@.la \ - libpolyp-mainloop-@PA_MAJORMINOR@.la \ - libpolyp-simple-@PA_MAJORMINOR@.la - -if HAVE_HOWL -lib_LTLIBRARIES += \ - libpolyp-browse-@PA_MAJORMINOR@.la -endif - -if HAVE_GLIB20 -lib_LTLIBRARIES += \ - libpolyp-mainloop-glib-@PA_MAJORMINOR@.la -endif - -if HAVE_GLIB12 -lib_LTLIBRARIES += \ - libpolyp-mainloop-glib12-@PA_MAJORMINOR@.la -endif - -libpolyp_@PA_MAJORMINOR@_la_SOURCES = \ - authkey.c authkey.h \ - cdecl.h \ - client-conf.c client-conf.h \ - conf-parser.c conf-parser.h \ - dllmain.c \ - dynarray.c dynarray.h \ - gcc-printf.h \ - idxset.c idxset.h \ - iochannel.c iochannel.h \ - llist.h \ - log.c log.h \ - mainloop-api.c mainloop-api.h \ - mcalign.c mcalign.h \ - memblock.c memblock.h \ - memchunk.c memchunk.h \ - native-common.h \ - packet.c packet.h \ - parseaddr.c parseaddr.h \ - pdispatch.c pdispatch.h \ - polyplib.h \ - polyplib-context.c polyplib-context.h \ - polyplib-def.h \ - polyplib-internal.h \ - polyplib-introspect.c polyplib-introspect.h \ - polyplib-operation.c polyplib-operation.h \ - polyplib-scache.c polyplib-scache.h \ - polyplib-stream.c polyplib-stream.h \ - polyplib-subscribe.c polyplib-subscribe.h \ - pstream.c pstream.h \ - pstream-util.c pstream-util.h \ - queue.c queue.h \ - random.c random.h \ - sample.c sample.h \ - socket-client.c socket-client.h \ - socket-util.c socket-util.h \ - strbuf.c strbuf.h \ - strlist.c strlist.h \ - tagstruct.c tagstruct.h \ - util.c util.h \ - winsock.h \ - xmalloc.c xmalloc.h \ - channelmap.c channelmap.h \ - volume.c volume.h - -if HAVE_X11 -libpolyp_@PA_MAJORMINOR@_la_SOURCES += \ - client-conf-x11.c client-conf-x11.h \ - x11prop.c x11prop.h -endif - -libpolyp_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) -libpolyp_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 -libpolyp_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) - -if HAVE_X11 -libpolyp_@PA_MAJORMINOR@_la_CFLAGS += $(X_CFLAGS) -libpolyp_@PA_MAJORMINOR@_la_LDFLAGS += $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) -endif - -if HAVE_LIBASYNCNS -libpolyp_@PA_MAJORMINOR@_la_CFLAGS += $(LIBASYNCNS_CFLAGS) -libpolyp_@PA_MAJORMINOR@_la_LIBADD += $(LIBASYNCNS_LIBS) -endif - -libpolyp_error_@PA_MAJORMINOR@_la_SOURCES = 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_mainloop_@PA_MAJORMINOR@_la_SOURCES = \ - mainloop.c mainloop.h \ - mainloop-api.h mainloop-api.c \ - mainloop-signal.c mainloop-signal.h \ - poll.c poll.h -libpolyp_mainloop_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) -libpolyp_mainloop_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la $(WINSOCK_LIBS) -libpolyp_mainloop_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 - -libpolyp_simple_@PA_MAJORMINOR@_la_SOURCES = 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 - -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 - -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 - -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 - -################################### -# Daemon core library # -################################### - -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 = \ - autoload.c autoload.h \ - cli-command.c cli-command.h \ - cli-text.c cli-text.h \ - client.c client.h \ - core.c core.h \ - dllmain.c \ - dynarray.c dynarray.h \ - endianmacros.h \ - g711.c g711.h \ - hashmap.c hashmap.h \ - idxset.c idxset.h \ - log.c log.h \ - mainloop.c mainloop.h \ - mainloop-api.c mainloop-api.h \ - mainloop-signal.c mainloop-signal.h \ - mcalign.c mcalign.h \ - memblock.c memblock.h \ - memblockq.c memblockq.h \ - memchunk.c memchunk.h \ - modargs.c modargs.h \ - modinfo.c modinfo.h \ - module.c module.h \ - namereg.c namereg.h \ - play-memchunk.c play-memchunk.h \ - poll.c poll.h \ - props.c props.h \ - queue.c queue.h \ - random.c random.h \ - resampler.c resampler.h \ - sample.c sample.h \ - sample-util.c sample-util.h \ - scache.c scache.h \ - sconv.c sconv.h \ - sconv-s16be.c sconv-s16be.h \ - sconv-s16le.c sconv-s16le.h \ - sink.c sink.h \ - sink-input.c sink-input.h \ - sioman.c sioman.h \ - sound-file.c sound-file.h \ - sound-file-stream.c sound-file-stream.h \ - source.c source.h \ - source-output.c source-output.h \ - strbuf.c strbuf.h \ - subscribe.c subscripe.h \ - tokenizer.c tokenizer.h \ - util.c util.h \ - winsock.h \ - xmalloc.c xmalloc.h \ - volume.c volume.h \ - channelmap.c channelmap.h - -libpolypcore_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBOIL_CFLAGS) -libpolypcore_la_LDFLAGS = -avoid-version -libpolypcore_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(WINSOCK_LIBS) $(LIBOIL_LIBS) - -################################### -# Plug-in support libraries # -################################### - -### Warning! Due to an obscure bug in libtool/automake it is required -### that the libraries in modlib_LTLIBRARIES are specified in-order, -### i.e. libraries near the end of the list depend on libraries near -### the head, and not the other way! - -modlib_LTLIBRARIES = \ - libsocket-util.la \ - libiochannel.la \ - libsocket-server.la \ - libsocket-client.la \ - libparseaddr.la \ - libpacket.la \ - libpstream.la \ - libioline.la \ - libcli.la \ - libprotocol-cli.la \ - libtagstruct.la \ - libpstream-util.la \ - libpdispatch.la \ - libauthkey.la \ - libauthkey-prop.la \ - libstrlist.la \ - libprotocol-simple.la \ - libprotocol-esound.la \ - libprotocol-native.la \ - libprotocol-http.la - -if HAVE_X11 -modlib_LTLIBRARIES += \ - libx11wrap.la \ - libx11prop.la -endif - -if HAVE_OSS -modlib_LTLIBRARIES += \ - liboss-util.la -endif - -if HAVE_ALSA -modlib_LTLIBRARIES += \ - libalsa-util.la -endif - -if HAVE_HOWL -modlib_LTLIBRARIES += \ - libhowl-wrap.la -endif - -libprotocol_simple_la_SOURCES = protocol-simple.c protocol-simple.h -libprotocol_simple_la_LDFLAGS = -avoid-version -libprotocol_simple_la_LIBADD = $(AM_LIBADD) libpolypcore.la libsocket-server.la libiochannel.la - -libsocket_server_la_SOURCES = \ - inet_ntop.c inet_ntop.h \ - socket-server.c socket-server.h -libsocket_server_la_LDFLAGS = -avoid-version -libsocket_server_la_LIBADD = $(AM_LIBADD) libpolypcore.la libiochannel.la libsocket-util.la $(LIBWRAP_LIBS) $(WINSOCK_LIBS) - -libsocket_client_la_SOURCES = socket-client.c socket-client.h -libsocket_client_la_LDFLAGS = -avoid-version -libsocket_client_la_LIBADD = $(AM_LIBADD) libpolypcore.la libiochannel.la libsocket-util.la libparseaddr.la $(LIBASYNCNS_LIBS) $(WINSOCK_LIBS) -libsocket_client_la_CFLAGS = $(AM_CFLAGS) $(LIBASYNCNS_CFLAGS) - -libparseaddr_la_SOURCES = parseaddr.c parseaddr.h -libparseaddr_la_LDFLAGS = -avoid-version -libparseaddr_la_LIBADD = $(AM_LIBADD) libpolypcore.la - -libpstream_la_SOURCES = pstream.c pstream.h -libpstream_la_LDFLAGS = -avoid-version -libpstream_la_LIBADD = $(AM_LIBADD) libpolypcore.la libpacket.la libiochannel.la $(WINSOCK_LIBS) - -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 libpolypcore.la - -libiochannel_la_SOURCES = iochannel.c iochannel.h -libiochannel_la_LDFLAGS = -avoid-version -libiochannel_la_LIBADD = $(AM_LIBADD) libsocket-util.la libpolypcore.la $(WINSOCK_LIBS) - -libpacket_la_SOURCES = packet.c packet.h -libpacket_la_LDFLAGS = -avoid-version -libpacket_la_LIBADD = $(AM_LIBADD) libpolypcore.la - -libioline_la_SOURCES = ioline.c ioline.h -libioline_la_LDFLAGS = -avoid-version -libioline_la_LIBADD = $(AM_LIBADD) libiochannel.la libpolypcore.la - -libcli_la_SOURCES = cli.c cli.h -libcli_la_CPPFLAGS = $(AM_CPPFLAGS) -libcli_la_LDFLAGS = -avoid-version -libcli_la_LIBADD = $(AM_LIBADD) libiochannel.la libioline.la libpolypcore.la - -libstrlist_la_SOURCES = strlist.c strlist.h -libstrlist_la_LDFLAGS = -avoid-version -libstrlist_la_LIBADD = $(AM_LIBADD) libpolypcore.la - -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 libpolypcore.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 libpolypcore.la libiochannel.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 libpolypcore.la libiochannel.la - -libtagstruct_la_SOURCES = tagstruct.c tagstruct.h -libtagstruct_la_LDFLAGS = -avoid-version -libtagstruct_la_LIBADD = $(AM_LIBADD) libpolypcore.la $(WINSOCK_LIBS) - -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 libpolypcore.la - -libauthkey_la_SOURCES = authkey.c authkey.h -libauthkey_la_LDFLAGS = -avoid-version -libauthkey_la_LIBADD = $(AM_LIBADD) libpolypcore.la - -libauthkey_prop_la_SOURCES = authkey-prop.c authkey-prop.h -libauthkey_prop_la_LDFLAGS = -avoid-version -libauthkey_prop_la_LIBADD = $(AM_LIBADD) libpolypcore.la - -libsocket_util_la_SOURCES = socket-util.c socket-util.h -libsocket_util_la_LDFLAGS = -avoid-version -libsocket_util_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) - -# X11 - -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) - -# OSS - -liboss_util_la_SOURCES = oss-util.c oss-util.h -liboss_util_la_LDFLAGS = -avoid-version - -# ALSA - -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) - -# HOWL - -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) - -################################### -# Plug-in libraries # -################################### - -modlib_LTLIBRARIES += \ - module-cli.la \ - module-cli-protocol-tcp.la \ - module-cli-protocol-tcp6.la \ - module-simple-protocol-tcp.la \ - module-simple-protocol-tcp6.la \ - module-esound-protocol-tcp.la \ - module-esound-protocol-tcp6.la \ - module-native-protocol-tcp.la \ - module-native-protocol-tcp6.la \ - module-native-protocol-fd.la \ - module-sine.la \ - module-combine.la \ - module-tunnel-sink.la \ - module-tunnel-source.la \ - module-null-sink.la \ - module-esound-sink.la \ - module-http-protocol-tcp.la \ - module-http-protocol-tcp6.la \ - module-detect.la - -if HAVE_AF_UNIX -modlib_LTLIBRARIES += \ - module-cli-protocol-unix.la \ - module-simple-protocol-unix.la \ - module-esound-protocol-unix.la \ - module-native-protocol-unix.la \ - module-http-protocol-unix.la -endif - -if HAVE_MKFIFO -modlib_LTLIBRARIES += \ - module-pipe-sink.la \ - module-pipe-source.la -endif - -if !OS_IS_WIN32 -modlib_LTLIBRARIES += \ - module-esound-compat-spawnfd.la \ - module-esound-compat-spawnpid.la -endif - -if HAVE_REGEX -modlib_LTLIBRARIES += \ - module-match.la -endif - -if HAVE_X11 -modlib_LTLIBRARIES += \ - module-x11-bell.la \ - module-x11-publish.la -endif - -if HAVE_OSS -modlib_LTLIBRARIES += \ - module-oss.la \ - module-oss-mmap.la -endif - -if HAVE_ALSA -modlib_LTLIBRARIES += \ - module-alsa-sink.la \ - module-alsa-source.la -endif - -if HAVE_SOLARIS -modlib_LTLIBRARIES += \ - module-solaris.la -endif - -if HAVE_HOWL -modlib_LTLIBRARIES += \ - module-zeroconf-publish.la -endif - -if HAVE_LIRC -modlib_LTLIBRARIES += \ - module-lirc.la -endif - -if HAVE_EVDEV -modlib_LTLIBRARIES += \ - module-mmkbd-evdev.la -endif - -if OS_IS_WIN32 -modlib_LTLIBRARIES += \ - module-waveout.la -endif - -# These are generated by a M4 script - -SYMDEF_FILES = \ - 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-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 \ - module-x11-bell-symdef.h \ - module-x11-publish-symdef.h \ - module-oss-symdef.h \ - module-oss-mmap-symdef.h \ - module-alsa-sink-symdef.h \ - module-alsa-source-symdef.h \ - module-solaris-symdef.h \ - module-waveout-symdef.h \ - module-detect-symdef.h - -EXTRA_DIST += $(SYMDEF_FILES) -BUILT_SOURCES += $(SYMDEF_FILES) - -$(SYMDEF_FILES): module-defs.h.m4 - $(M4) -Dfname="$@" $< > $@ - -# Simple protocol - -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) libpolypcore.la 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) libpolypcore.la 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) libpolypcore.la libprotocol-simple.la libsocket-server.la libsocket-util.la - -# CLI protocol - -module_cli_la_SOURCES = module-cli.c -module_cli_la_LDFLAGS = -module -avoid-version -module_cli_la_LIBADD = $(AM_LIBADD) libcli.la libiochannel.la libpolypcore.la - -module_cli_protocol_tcp_la_SOURCES = module-protocol-stub.c -module_cli_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS) -module_cli_protocol_tcp_la_LDFLAGS = -module -avoid-version -module_cli_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-cli.la libsocket-server.la - -module_cli_protocol_tcp6_la_SOURCES = module-protocol-stub.c -module_cli_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS) -module_cli_protocol_tcp6_la_LDFLAGS = -module -avoid-version -module_cli_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-cli.la libsocket-server.la - -module_cli_protocol_unix_la_SOURCES = module-protocol-stub.c -module_cli_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS) -module_cli_protocol_unix_la_LDFLAGS = -module -avoid-version -module_cli_protocol_unix_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-cli.la libsocket-server.la libsocket-util.la - -# HTTP protocol - -module_http_protocol_tcp_la_SOURCES = module-protocol-stub.c -module_http_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS) -module_http_protocol_tcp_la_LDFLAGS = -module -avoid-version -module_http_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-http.la libsocket-server.la - -module_http_protocol_tcp6_la_SOURCES = module-protocol-stub.c -module_http_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS) -module_http_protocol_tcp6_la_LDFLAGS = -module -avoid-version -module_http_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-http.la libsocket-server.la - -module_http_protocol_unix_la_SOURCES = module-protocol-stub.c -module_http_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS) -module_http_protocol_unix_la_LDFLAGS = -module -avoid-version -module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-http.la libsocket-server.la libsocket-util.la - -# Native protocol - -module_native_protocol_tcp_la_SOURCES = module-protocol-stub.c -module_native_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS) -module_native_protocol_tcp_la_LDFLAGS = -module -avoid-version -module_native_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-native.la libsocket-server.la - -module_native_protocol_tcp6_la_SOURCES = module-protocol-stub.c -module_native_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS) -module_native_protocol_tcp6_la_LDFLAGS = -module -avoid-version -module_native_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-native.la libsocket-server.la - -module_native_protocol_unix_la_SOURCES = module-protocol-stub.c -module_native_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS) -module_native_protocol_unix_la_LDFLAGS = -module -avoid-version -module_native_protocol_unix_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-native.la libsocket-server.la libsocket-util.la - -module_native_protocol_fd_la_SOURCES = module-native-protocol-fd.c -module_native_protocol_fd_la_CFLAGS = $(AM_CFLAGS) -module_native_protocol_fd_la_LDFLAGS = -module -avoid-version -module_native_protocol_fd_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-native.la libsocket-server.la libsocket-util.la libiochannel.la - -# EsounD protocol - -module_esound_protocol_tcp_la_SOURCES = module-protocol-stub.c -module_esound_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS) -module_esound_protocol_tcp_la_LDFLAGS = -module -avoid-version -module_esound_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-esound.la libsocket-server.la - -module_esound_protocol_tcp6_la_SOURCES = module-protocol-stub.c -module_esound_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS) -module_esound_protocol_tcp6_la_LDFLAGS = -module -avoid-version -module_esound_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-esound.la libsocket-server.la - -module_esound_protocol_unix_la_SOURCES = module-protocol-stub.c -module_esound_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS) -module_esound_protocol_unix_la_LDFLAGS = -module -avoid-version -module_esound_protocol_unix_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-esound.la libsocket-server.la libsocket-util.la - -module_esound_compat_spawnfd_la_SOURCES = module-esound-compat-spawnfd.c -module_esound_compat_spawnfd_la_LDFLAGS = -module -avoid-version -module_esound_compat_spawnfd_la_LIBADD = $(AM_LIBADD) libpolypcore.la - -module_esound_compat_spawnpid_la_SOURCES = module-esound-compat-spawnpid.c -module_esound_compat_spawnpid_la_LDFLAGS = -module -avoid-version -module_esound_compat_spawnpid_la_LIBADD = $(AM_LIBADD) libpolypcore.la - -module_esound_sink_la_SOURCES = module-esound-sink.c -module_esound_sink_la_LDFLAGS = -module -avoid-version -module_esound_sink_la_LIBADD = $(AM_LIBADD) libpolypcore.la libiochannel.la libsocket-client.la libauthkey.la - -# Pipes - -module_pipe_sink_la_SOURCES = module-pipe-sink.c -module_pipe_sink_la_LDFLAGS = -module -avoid-version -module_pipe_sink_la_LIBADD = $(AM_LIBADD) libpolypcore.la libiochannel.la - -module_pipe_source_la_SOURCES = module-pipe-source.c -module_pipe_source_la_LDFLAGS = -module -avoid-version -module_pipe_source_la_LIBADD = $(AM_LIBADD) libpolypcore.la libiochannel.la - -# Fake sources/sinks - -module_sine_la_SOURCES = module-sine.c -module_sine_la_LDFLAGS = -module -avoid-version -module_sine_la_LIBADD = $(AM_LIBADD) libpolypcore.la - -module_null_sink_la_SOURCES = module-null-sink.c -module_null_sink_la_LDFLAGS = -module -avoid-version -module_null_sink_la_LIBADD = $(AM_LIBADD) libpolypcore.la - -# Couplings - -module_combine_la_SOURCES = module-combine.c -module_combine_la_LDFLAGS = -module -avoid-version -module_combine_la_LIBADD = $(AM_LIBADD) libpolypcore.la - -module_match_la_SOURCES = module-match.c -module_match_la_LDFLAGS = -module -avoid-version -module_match_la_LIBADD = $(AM_LIBADD) libpolypcore.la - -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) libpolypcore.la libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libsocket-util.la libiochannel.la - -module_tunnel_source_la_SOURCES = module-tunnel.c -module_tunnel_source_la_LDFLAGS = -module -avoid-version -module_tunnel_source_la_LIBADD = $(AM_LIBADD) libpolypcore.la libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libsocket-util.la libiochannel.la - -# X11 - -module_x11_bell_la_SOURCES = 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 - -# OSS - -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 - -# ALSA - -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) - -# Solaris - -module_solaris_la_SOURCES = module-solaris.c -module_solaris_la_LDFLAGS = -module -avoid-version -module_solaris_la_LIBADD = $(AM_LIBADD) libiochannel.la - -# HOWL - -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) - -# LIRC - -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) - -# Linux evdev - -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) - -# Windows waveout - -module_waveout_la_SOURCES = module-waveout.c -module_waveout_la_LDFLAGS = -module -avoid-version -module_waveout_la_LIBADD = $(AM_LIBADD) libpolypcore.la -lwinmm -module_waveout_la_CFLAGS = $(AM_CFLAGS) - -# Hardware autodetection module -module_detect_la_SOURCES = module-detect.c -module_detect_la_LDFLAGS = -module -avoid-version -module_detect_la_LIBADD = $(AM_LIBADD) -module_detect_la_CFLAGS = $(AM_CFLAGS) - -################################### -# Some minor stuff # -################################### - -suid: polypaudio - chown root $< - chmod u+s $< - -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\@,$(POLYPAUDIO_BINARY),g' < $< > $@ - -client.conf: client.conf.in Makefile - sed -e 's,@POLYPAUDIO_BINARY\@,$(POLYPAUDIO_BINARY),g' < $< > $@ - -if OS_IS_WIN32 -default.pa: default.pa.win32 - cp $< $@ -else -default.pa: default.pa.in Makefile - sed -e 's,@POLYPAUDIO_BINARY\@,$(POLYPAUDIO_BINARY),g' < $< > $@ -endif - -daemon.conf: daemon.conf.in Makefile - sed -e 's,@DLSEARCHPATH\@,$(modlibdir),g' \ - -e 's,@DEFAULT_CONFIG_FILE\@,$(DEFAULT_CONFIG_DIR),g' < $< > $@ - -install-exec-hook: - chown root $(DESTDIR)$(bindir)/polypaudio ; true - chmod u+s $(DESTDIR)$(bindir)/polypaudio - ln -sf pacat $(DESTDIR)$(bindir)/parec diff --git a/polyp/alsa-util.c b/polyp/alsa-util.c deleted file mode 100644 index cc65b8de..00000000 --- a/polyp/alsa-util.c +++ /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 -#endif - -#include -#include - -#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 pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size) { - int ret = -1; - snd_pcm_uframes_t buffer_size; - snd_pcm_hw_params_t *hwparams = NULL; - static const snd_pcm_format_t format_trans[] = { - [PA_SAMPLE_U8] = SND_PCM_FORMAT_U8, - [PA_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW, - [PA_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW, - [PA_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE, - [PA_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE, - [PA_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE, - [PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE, - }; - assert(pcm_handle && ss && periods && period_size); - - if (snd_pcm_hw_params_malloc(&hwparams) < 0 || - snd_pcm_hw_params_any(pcm_handle, hwparams) < 0 || - snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 || - snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[ss->format]) < 0 || - snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &ss->rate, NULL) < 0 || - snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss->channels) < 0 || - (*periods > 0 && snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, periods, NULL) < 0) || - (*period_size > 0 && snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, period_size, NULL) < 0) || - snd_pcm_hw_params(pcm_handle, hwparams) < 0) - goto finish; - - if (snd_pcm_prepare(pcm_handle) < 0) - goto finish; - - if (snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size) < 0 || - snd_pcm_hw_params_get_period_size(hwparams, period_size, NULL) < 0) - goto finish; - - assert(buffer_size > 0); - assert(*period_size > 0); - *periods = buffer_size / *period_size; - assert(*periods > 0); - - ret = 0; - -finish: - if (hwparams) - snd_pcm_hw_params_free(hwparams); - - return ret; -} - -/* Allocate an IO event for every ALSA poll descriptor for the - * specified ALSA device. Return a pointer to such an array in - * *io_events. Store the length of that array in *n_io_events. Use the - * specified callback function and userdata. The array has to be freed - * with pa_free_io_events(). */ -int pa_create_io_events(snd_pcm_t *pcm_handle, pa_mainloop_api* m, pa_io_event ***io_events, unsigned *n_io_events, void (*cb)(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata), void *userdata) { - unsigned i; - struct pollfd *pfds, *ppfd; - pa_io_event **ios; - assert(pcm_handle && m && io_events && n_io_events && cb); - - *n_io_events = snd_pcm_poll_descriptors_count(pcm_handle); - - pfds = pa_xmalloc(sizeof(struct pollfd) * *n_io_events); - if (snd_pcm_poll_descriptors(pcm_handle, pfds, *n_io_events) < 0) { - pa_xfree(pfds); - return -1; - } - - *io_events = pa_xmalloc(sizeof(void*) * *n_io_events); - - for (i = 0, ios = *io_events, ppfd = pfds; i < *n_io_events; i++, ios++, ppfd++) { - *ios = m->io_new(m, ppfd->fd, - ((ppfd->events & POLLIN) ? PA_IO_EVENT_INPUT : 0) | - ((ppfd->events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0), cb, userdata); - assert(*ios); - } - - pa_xfree(pfds); - return 0; -} - -/* Free the memory allocated by pa_create_io_events() */ -void pa_free_io_events(pa_mainloop_api* m, pa_io_event **io_events, unsigned n_io_events) { - unsigned i; - pa_io_event **ios; - assert(m && io_events); - - for (ios = io_events, i = 0; i < n_io_events; i++, ios++) - m->io_free(*ios); - pa_xfree(io_events); -} diff --git a/polyp/alsa-util.h b/polyp/alsa-util.h deleted file mode 100644 index d180db3e..00000000 --- a/polyp/alsa-util.h +++ /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 - -#include "sample.h" -#include "mainloop-api.h" - -int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, const pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size); - -int pa_create_io_events(snd_pcm_t *pcm_handle, pa_mainloop_api *m, pa_io_event ***io_events, unsigned *n_io_events, void (*cb)(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata), void *userdata); -void pa_free_io_events(pa_mainloop_api* m, pa_io_event **io_sources, unsigned n_io_sources); - -#endif diff --git a/polyp/authkey-prop.c b/polyp/authkey-prop.c deleted file mode 100644 index 8657f5a5..00000000 --- a/polyp/authkey-prop.c +++ /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 -#include - -#include "xmalloc.h" -#include "authkey-prop.h" -#include "props.h" -#include "log.h" - -struct authkey_data { - int ref; - size_t length; -}; - -int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len) { - struct authkey_data *a; - assert(c && name && data && len > 0); - - if (!(a = pa_property_get(c, name))) - return -1; - - assert(a->length == len); - memcpy(data, a+1, len); - return 0; -} - -int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len) { - struct authkey_data *a; - assert(c && name); - - if (pa_property_get(c, name)) - return -1; - - a = pa_xmalloc(sizeof(struct authkey_data) + len); - a->ref = 1; - a->length = len; - memcpy(a+1, data, len); - - pa_property_set(c, name, a); - - return 0; -} - -void pa_authkey_prop_ref(pa_core *c, const char *name) { - struct authkey_data *a; - assert(c && name); - - a = pa_property_get(c, name); - assert(a && a->ref >= 1); - - a->ref++; -} - -void pa_authkey_prop_unref(pa_core *c, const char *name) { - struct authkey_data *a; - assert(c && name); - - a = pa_property_get(c, name); - assert(a && a->ref >= 1); - - if (!(--a->ref)) { - pa_property_remove(c, name); - pa_xfree(a); - } -} - - diff --git a/polyp/authkey-prop.h b/polyp/authkey-prop.h deleted file mode 100644 index 29b40bb2..00000000 --- a/polyp/authkey-prop.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef fooauthkeyprophfoo -#define fooauthkeyprophfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "core.h" - -/* The authkey-prop uses a central property to store a previously - * loaded cookie in memory. Useful for sharing the same cookie between - * several modules. */ - -/* Return the data of the specified authorization key property. Doesn't alter the refernce count of the key */ -int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len); - -/* Store data in the specified authorization key property. The initial reference count is set to 1 */ -int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len); - -/* Increase the reference count of the specified authorization key */ -void pa_authkey_prop_ref(pa_core *c, const char *name); - -/* Decrease the reference count of the specified authorization key */ -void pa_authkey_prop_unref(pa_core *c, const char *name); - -#endif diff --git a/polyp/authkey.c b/polyp/authkey.c deleted file mode 100644 index 969f09d9..00000000 --- a/polyp/authkey.c +++ /dev/null @@ -1,201 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "authkey.h" -#include "util.h" -#include "log.h" -#include "random.h" - -/* Generate a new authorization key, store it in file fd and return it in *data */ -static int generate(int fd, void *ret_data, size_t length) { - ssize_t r; - assert(fd >= 0 && ret_data && length); - - pa_random(ret_data, length); - - lseek(fd, 0, SEEK_SET); - ftruncate(fd, 0); - - if ((r = pa_loop_write(fd, ret_data, length)) < 0 || (size_t) r != length) { - pa_log(__FILE__": failed to write cookie file: %s\n", strerror(errno)); - return -1; - } - - return 0; -} - -/* Load an euthorization cookie from file fn and store it in data. If - * the cookie file doesn't exist, create it */ -static int load(const char *fn, void *data, size_t length) { - int fd = -1; - int writable = 1; - int unlock = 0, ret = -1; - ssize_t r; - assert(fn && data && length); - - if ((fd = open(fn, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) { - if (errno != EACCES || (fd = open(fn, O_RDONLY)) < 0) { - pa_log(__FILE__": failed to open cookie file '%s': %s\n", fn, strerror(errno)); - goto finish; - } else - writable = 0; - } - - unlock = pa_lock_fd(fd, 1) >= 0; - - if ((r = pa_loop_read(fd, data, length)) < 0) { - pa_log(__FILE__": failed to read cookie file '%s': %s\n", fn, strerror(errno)); - goto finish; - } - - if ((size_t) r != length) { - - if (!writable) { - pa_log(__FILE__": unable to write cookie to read only file\n"); - goto finish; - } - - if (generate(fd, data, length) < 0) - goto finish; - } - - ret = 0; - -finish: - - if (fd >= 0) { - - if (unlock) - pa_lock_fd(fd, 0); - - close(fd); - } - - return ret; -} - -/* Load a cookie from a cookie file. If the file doesn't exist, create it. */ -int pa_authkey_load(const char *path, void *data, size_t length) { - int ret; - - assert(path && data && length); - - ret = load(path, data, length); - - if (ret < 0) - pa_log(__FILE__": Failed to load authorization key '%s': %s\n", path, - (ret == -1) ? strerror(errno) : "file corrupt"); - - return ret; -} - -/* If the specified file path starts with / return it, otherwise - * return path prepended with home directory */ -static const char *normalize_path(const char *fn, char *s, size_t l) { - assert(fn && s && l > 0); - -#ifndef OS_IS_WIN32 - if (fn[0] != '/') { -#else - if (strlen(fn) < 3 || !isalpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') { -#endif - char homedir[PATH_MAX]; - if (!pa_get_home_dir(homedir, sizeof(homedir))) - return NULL; - -#ifndef OS_IS_WIN32 - snprintf(s, l, "%s/%s", homedir, fn); -#else - snprintf(s, l, "%s\\%s", homedir, fn); -#endif - return s; - } - - return fn; -} - -/* Load a cookie from a file in the home directory. If the specified - * path starts with /, use it as absolute path instead. */ -int pa_authkey_load_auto(const char *fn, void *data, size_t length) { - char path[PATH_MAX]; - const char *p; - assert(fn && data && length); - - if (!(p = normalize_path(fn, path, sizeof(path)))) - return -2; - - return pa_authkey_load(p, data, length); -} - -/* Store the specified cookie in the speicified cookie file */ -int pa_authkey_save(const char *fn, const void *data, size_t length) { - int fd = -1; - int unlock = 0, ret = -1; - ssize_t r; - char path[PATH_MAX]; - const char *p; - assert(fn && data && length); - - if (!(p = normalize_path(fn, path, sizeof(path)))) - return -2; - - if ((fd = open(p, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) { - pa_log(__FILE__": failed to open cookie file '%s': %s\n", fn, strerror(errno)); - goto finish; - } - - unlock = pa_lock_fd(fd, 1) >= 0; - - if ((r = pa_loop_write(fd, data, length)) < 0 || (size_t) r != length) { - pa_log(__FILE__": failed to read cookie file '%s': %s\n", fn, strerror(errno)); - goto finish; - } - - ret = 0; - -finish: - - if (fd >= 0) { - - if (unlock) - pa_lock_fd(fd, 0); - - close(fd); - } - - return ret; -} diff --git a/polyp/authkey.h b/polyp/authkey.h deleted file mode 100644 index 81b1a578..00000000 --- a/polyp/authkey.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef fooauthkeyhfoo -#define fooauthkeyhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -int pa_authkey_load(const char *path, void *data, size_t len); -int pa_authkey_load_auto(const char *fn, void *data, size_t length); - -int pa_authkey_save(const char *path, const void *data, size_t length); - -#endif diff --git a/polyp/autoload.c b/polyp/autoload.c deleted file mode 100644 index ff2916cb..00000000 --- a/polyp/autoload.c +++ /dev/null @@ -1,180 +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 -#endif - -#include -#include -#include - -#include "autoload.h" -#include "module.h" -#include "xmalloc.h" -#include "memchunk.h" -#include "sound-file.h" -#include "log.h" -#include "scache.h" -#include "subscribe.h" - -static void entry_free(pa_autoload_entry *e) { - assert(e); - pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_REMOVE, PA_INVALID_INDEX); - pa_xfree(e->name); - pa_xfree(e->module); - pa_xfree(e->argument); - pa_xfree(e); -} - -static void entry_remove_and_free(pa_autoload_entry *e) { - assert(e && e->core); - - pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL); - pa_hashmap_remove(e->core->autoload_hashmap, e->name); - entry_free(e); -} - -static pa_autoload_entry* entry_new(pa_core *c, const char *name) { - pa_autoload_entry *e = NULL; - assert(c && name); - - if (c->autoload_hashmap && (e = pa_hashmap_get(c->autoload_hashmap, name))) - return NULL; - - e = pa_xmalloc(sizeof(pa_autoload_entry)); - e->core = c; - e->name = pa_xstrdup(name); - e->module = e->argument = NULL; - e->in_action = 0; - - if (!c->autoload_hashmap) - c->autoload_hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - assert(c->autoload_hashmap); - - pa_hashmap_put(c->autoload_hashmap, e->name, e); - - if (!c->autoload_idxset) - c->autoload_idxset = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - pa_idxset_put(c->autoload_idxset, e, &e->index); - - pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_NEW, e->index); - - return e; -} - -int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx) { - pa_autoload_entry *e = NULL; - assert(c && name && module && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE)); - - if (!(e = entry_new(c, name))) - return -1; - - e->module = pa_xstrdup(module); - e->argument = pa_xstrdup(argument); - e->type = type; - - if (idx) - *idx = e->index; - - return 0; -} - -int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type) { - pa_autoload_entry *e; - assert(c && name && type); - - if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type) - return -1; - - entry_remove_and_free(e); - return 0; -} - -int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) { - pa_autoload_entry *e; - assert(c && idx != PA_IDXSET_INVALID); - - if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx))) - return -1; - - entry_remove_and_free(e); - return 0; -} - -void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) { - pa_autoload_entry *e; - pa_module *m; - assert(c && name); - - if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || (e->type != type)) - return; - - if (e->in_action) - return; - - e->in_action = 1; - - if (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) { - if ((m = pa_module_load(c, e->module, e->argument))) - m->auto_unload = 1; - } - - e->in_action = 0; -} - -static void free_func(void *p, PA_GCC_UNUSED void *userdata) { - pa_autoload_entry *e = p; - pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL); - entry_free(e); -} - -void pa_autoload_free(pa_core *c) { - if (c->autoload_hashmap) { - pa_hashmap_free(c->autoload_hashmap, free_func, NULL); - c->autoload_hashmap = NULL; - } - - if (c->autoload_idxset) { - pa_idxset_free(c->autoload_idxset, NULL, NULL); - c->autoload_idxset = NULL; - } -} - -const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type) { - pa_autoload_entry *e; - assert(c && name); - - if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type) - return NULL; - - return e; -} - -const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx) { - pa_autoload_entry *e; - assert(c && idx != PA_IDXSET_INVALID); - - if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx))) - return NULL; - - return e; -} diff --git a/polyp/autoload.h b/polyp/autoload.h deleted file mode 100644 index 7350c16a..00000000 --- a/polyp/autoload.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef fooautoloadhfoo -#define fooautoloadhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "namereg.h" - -/* Using the autoloading facility, modules by be loaded on-demand and - * synchronously. The user may register a "ghost sink" or "ghost - * source". Whenever this sink/source is requested but not available a - * specified module is loaded. */ - -/* An autoload entry, or "ghost" sink/source */ -typedef struct pa_autoload_entry { - pa_core *core; - uint32_t index; - char *name; - pa_namereg_type_t type; /* Type of the autoload entry */ - int in_action; /* Currently loaded */ - char *module, *argument; -} pa_autoload_entry; - -/* Add a new autoload entry of the given time, with the speicified - * sink/source name, module name and argument. Return the entry's - * index in *index */ -int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx); - -/* Free all autoload entries */ -void pa_autoload_free(pa_core *c); -int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type); -int pa_autoload_remove_by_index(pa_core *c, uint32_t idx); - -/* Request an autoload entry by its name, effectively causing a module to be loaded */ -void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type); - -const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type); -const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx); - -#endif diff --git a/polyp/caps.c b/polyp/caps.c deleted file mode 100644 index 4ecb5848..00000000 --- a/polyp/caps.c +++ /dev/null @@ -1,131 +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 -#endif - -#include -#include -#include -#include - -#ifdef HAVE_SYS_CAPABILITY_H -#include -#endif - -#include "log.h" -#include "caps.h" - -#ifdef HAVE_GETUID - -/* Drop root rights when called SUID root */ -void pa_drop_root(void) { - uid_t uid = getuid(); - - if (uid == 0 || geteuid() != 0) - return; - - pa_log_info(__FILE__": dropping root rights.\n"); - -#if defined(HAVE_SETRESUID) - setresuid(uid, uid, uid); -#elif defined(HAVE_SETREUID) - setreuid(uid, uid); -#else - setuid(uid); - seteuid(uid); -#endif -} - -#else - -void pa_drop_root(void) { -} - -#endif - -#ifdef HAVE_SYS_CAPABILITY_H - -/* Limit capabilities set to CAPSYS_NICE */ -int pa_limit_caps(void) { - int r = -1; - cap_t caps; - cap_value_t nice_cap = CAP_SYS_NICE; - - caps = cap_init(); - assert(caps); - - cap_clear(caps); - - cap_set_flag(caps, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET); - cap_set_flag(caps, CAP_PERMITTED, 1, &nice_cap, CAP_SET); - - if (cap_set_proc(caps) < 0) - goto fail; - - pa_log_info(__FILE__": dropped capabilities successfully.\n"); - - r = 0; - -fail: - cap_free (caps); - - return r; -} - -/* Drop all capabilities, effectively becoming a normal user */ -int pa_drop_caps(void) { - cap_t caps; - int r = -1; - - caps = cap_init(); - assert(caps); - - cap_clear(caps); - - if (cap_set_proc(caps) < 0) { - pa_log(__FILE__": failed to drop capabilities: %s\n", strerror(errno)); - goto fail; - } - - r = 0; - -fail: - cap_free (caps); - - return r; -} - -#else - -/* NOOPs in case capabilities are not available. */ -int pa_limit_caps(void) { - return 0; -} - -int pa_drop_caps(void) { - pa_drop_root(); - return 0; -} - -#endif - diff --git a/polyp/caps.h b/polyp/caps.h deleted file mode 100644 index 3bb861d1..00000000 --- a/polyp/caps.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef foocapshfoo -#define foocapshfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -void pa_drop_root(void); -int pa_limit_caps(void); -int pa_drop_caps(void); - -#endif diff --git a/polyp/cdecl.h b/polyp/cdecl.h deleted file mode 100644 index d51ae026..00000000 --- a/polyp/cdecl.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef foocdeclhfoo -#define foocdeclhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -/** \file - * C++ compatibility support */ - -#ifdef __cplusplus -/** If using C++ this macro enables C mode, otherwise does nothing */ -#define PA_C_DECL_BEGIN extern "C" { -/** If using C++ this macros switches back to C++ mode, otherwise does nothing */ -#define PA_C_DECL_END } - -#else -/** If using C++ this macro enables C mode, otherwise does nothing */ -#define PA_C_DECL_BEGIN -/** If using C++ this macros switches back to C++ mode, otherwise does nothing */ -#define PA_C_DECL_END - -#endif - -#endif diff --git a/polyp/channelmap.c b/polyp/channelmap.c deleted file mode 100644 index 7bfd21e6..00000000 --- a/polyp/channelmap.c +++ /dev/null @@ -1,202 +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 -#endif - -#include -#include -#include -#include - -#include "channelmap.h" - -pa_channel_map* pa_channel_map_init(pa_channel_map *m) { - unsigned c; - assert(m); - - m->channels = 0; - - for (c = 0; c < PA_CHANNELS_MAX; c++) - m->map[c] = PA_CHANNEL_POSITION_INVALID; - - return m; -} - -pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) { - assert(m); - - pa_channel_map_init(m); - - m->channels = 1; - m->map[0] = PA_CHANNEL_POSITION_MONO; - return m; -} - -pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) { - assert(m); - - pa_channel_map_init(m); - - m->channels = 2; - m->map[0] = PA_CHANNEL_POSITION_LEFT; - m->map[1] = PA_CHANNEL_POSITION_RIGHT; - return m; -} - -pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels) { - assert(m); - assert(channels > 0); - assert(channels <= PA_CHANNELS_MAX); - - pa_channel_map_init(m); - - m->channels = channels; - - switch (channels) { - case 1: - m->map[0] = PA_CHANNEL_POSITION_MONO; - return m; - - case 8: - m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT; - m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT; - /* Fall through */ - - case 6: - m->map[5] = PA_CHANNEL_POSITION_LFE; - /* Fall through */ - - case 5: - m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; - /* Fall through */ - - case 4: - m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT; - m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; - /* Fall through */ - - case 2: - m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; - m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; - return m; - - default: - return NULL; - } -} - -const char* pa_channel_position_to_string(pa_channel_position_t pos) { - - const char *const table[] = { - [PA_CHANNEL_POSITION_MONO] = "mono", - - [PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center", - [PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left", - [PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right", - - [PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center", - [PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left", - [PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right", - - [PA_CHANNEL_POSITION_LFE] = "lfe", - - [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center", - [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center", - - [PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left", - [PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right", - - [PA_CHANNEL_POSITION_AUX1] = "aux1", - [PA_CHANNEL_POSITION_AUX2] = "aux2", - [PA_CHANNEL_POSITION_AUX3] = "aux3", - [PA_CHANNEL_POSITION_AUX4] = "aux4", - [PA_CHANNEL_POSITION_AUX5] = "aux5", - [PA_CHANNEL_POSITION_AUX6] = "aux6", - [PA_CHANNEL_POSITION_AUX7] = "aux7", - [PA_CHANNEL_POSITION_AUX8] = "aux8", - [PA_CHANNEL_POSITION_AUX9] = "aux9", - [PA_CHANNEL_POSITION_AUX10] = "aux10", - [PA_CHANNEL_POSITION_AUX11] = "aux11", - [PA_CHANNEL_POSITION_AUX12] = "aux12" - }; - - if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX) - return NULL; - - return table[pos]; -} - -int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) { - unsigned c; - - assert(a); - assert(b); - - if (a->channels != b->channels) - return 0; - - for (c = 0; c < a->channels; c++) - if (a->map[c] != b->map[c]) - return 0; - - return 1; -} - -char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) { - unsigned channel; - int first = 1; - char *e; - - assert(s); - assert(l > 0); - assert(map); - - *(e = s) = 0; - - for (channel = 0; channel < map->channels && l > 1; channel++) { - l -= snprintf(e, l, "%s%u:%s", - first ? "" : " ", - channel, - pa_channel_position_to_string(map->map[channel])); - - e = strchr(e, 0); - first = 0; - } - - return s; -} - -int pa_channel_map_valid(const pa_channel_map *map) { - unsigned c; - - assert(map); - - if (map->channels <= 0 || map->channels > PA_CHANNELS_MAX) - return 0; - - for (c = 0; c < map->channels; c++) - if (map->map[c] < 0 ||map->map[c] >= PA_CHANNEL_POSITION_MAX) - return 0; - - return 1; -} diff --git a/polyp/channelmap.h b/polyp/channelmap.h deleted file mode 100644 index 0b9f6e26..00000000 --- a/polyp/channelmap.h +++ /dev/null @@ -1,98 +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 -#include - -/** \file - * Constants and routines for channel mapping handling */ - -PA_C_DECL_BEGIN - -typedef enum { - PA_CHANNEL_POSITION_INVALID = -1, - PA_CHANNEL_POSITION_MONO = 0, - - PA_CHANNEL_POSITION_LEFT, - PA_CHANNEL_POSITION_RIGHT, - - PA_CHANNEL_POSITION_FRONT_CENTER, - PA_CHANNEL_POSITION_FRONT_LEFT = PA_CHANNEL_POSITION_LEFT, - PA_CHANNEL_POSITION_FRONT_RIGHT = PA_CHANNEL_POSITION_RIGHT, - - PA_CHANNEL_POSITION_REAR_CENTER, - PA_CHANNEL_POSITION_REAR_LEFT, - PA_CHANNEL_POSITION_REAR_RIGHT, - - PA_CHANNEL_POSITION_LFE, - PA_CHANNEL_POSITION_SUBWOOFER = PA_CHANNEL_POSITION_LFE, - - PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, - PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, - - PA_CHANNEL_POSITION_SIDE_LEFT, - PA_CHANNEL_POSITION_SIDE_RIGHT, - - PA_CHANNEL_POSITION_AUX0, - PA_CHANNEL_POSITION_AUX1, - PA_CHANNEL_POSITION_AUX2, - PA_CHANNEL_POSITION_AUX3, - PA_CHANNEL_POSITION_AUX4, - PA_CHANNEL_POSITION_AUX5, - PA_CHANNEL_POSITION_AUX6, - PA_CHANNEL_POSITION_AUX7, - PA_CHANNEL_POSITION_AUX8, - PA_CHANNEL_POSITION_AUX9, - PA_CHANNEL_POSITION_AUX10, - PA_CHANNEL_POSITION_AUX11, - PA_CHANNEL_POSITION_AUX12, - PA_CHANNEL_POSITION_AUX13, - PA_CHANNEL_POSITION_AUX14, - PA_CHANNEL_POSITION_AUX15, - - PA_CHANNEL_POSITION_MAX -} pa_channel_position_t; - -typedef struct pa_channel_map { - uint8_t channels; - pa_channel_position_t map[PA_CHANNELS_MAX]; -} pa_channel_map; - -pa_channel_map* pa_channel_map_init(pa_channel_map *m); -pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m); -pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m); -pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels); - -const char* pa_channel_position_to_string(pa_channel_position_t pos); - -#define PA_CHANNEL_MAP_SNPRINT_MAX 64 -char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map); - -int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b); - -int pa_channel_map_valid(const pa_channel_map *map); - -PA_C_DECL_END - -#endif diff --git a/polyp/cli-command.c b/polyp/cli-command.c deleted file mode 100644 index f6192bf8..00000000 --- a/polyp/cli-command.c +++ /dev/null @@ -1,834 +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 -#endif - -#include -#include -#include -#include -#include - -#include "cli-command.h" -#include "module.h" -#include "sink.h" -#include "source.h" -#include "client.h" -#include "sink-input.h" -#include "source-output.h" -#include "tokenizer.h" -#include "strbuf.h" -#include "namereg.h" -#include "cli-text.h" -#include "scache.h" -#include "sample-util.h" -#include "sound-file.h" -#include "play-memchunk.h" -#include "autoload.h" -#include "xmalloc.h" -#include "sound-file-stream.h" -#include "props.h" -#include "util.h" - -struct command { - const char *name; - int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, int *fail); - const char *help; - unsigned args; -}; - -#define INCLUDE_META ".include" -#define FAIL_META ".fail" -#define NOFAIL_META ".nofail" - -/* Prototypes for all available commands */ -static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); -static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); - - -/* A method table for all available commands */ - -static const struct command commands[] = { - { "exit", pa_cli_command_exit, "Terminate the daemon", 1 }, - { "help", pa_cli_command_help, "Show this help", 1 }, - { "list-modules", pa_cli_command_modules, "List loaded modules", 1 }, - { "list-sinks", pa_cli_command_sinks, "List loaded sinks", 1 }, - { "list-sources", pa_cli_command_sources, "List loaded sources", 1 }, - { "list-clients", pa_cli_command_clients, "List loaded clients", 1 }, - { "list-sink-inputs", pa_cli_command_sink_inputs, "List sink inputs", 1 }, - { "list-source-outputs", pa_cli_command_source_outputs, "List source outputs", 1 }, - { "stat", pa_cli_command_stat, "Show memory block statistics", 1 }, - { "info", pa_cli_command_info, "Show comprehensive status", 1 }, - { "ls", pa_cli_command_info, NULL, 1 }, - { "list", pa_cli_command_info, NULL, 1 }, - { "load-module", pa_cli_command_load, "Load a module (args: name, arguments)", 3}, - { "unload-module", pa_cli_command_unload, "Unload a module (args: index)", 2}, - { "set-sink-volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3}, - { "set-sink-input-volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index|name, volume)", 3}, - { "set-default-sink", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2}, - { "set-default-source", pa_cli_command_source_default, "Set the default source (args: index|name)", 2}, - { "kill-client", pa_cli_command_kill_client, "Kill a client (args: index)", 2}, - { "kill-sink-input", pa_cli_command_kill_sink_input, "Kill a sink input (args: index)", 2}, - { "kill-source-output", pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2}, - { "list-samples", pa_cli_command_scache_list, "List all entries in the sample cache", 1}, - { "play-sample", pa_cli_command_scache_play, "Play a sample from the sample cache (args: name, sink|index)", 3}, - { "remove-sample", pa_cli_command_scache_remove, "Remove a sample from the sample cache (args: name)", 2}, - { "load-sample", pa_cli_command_scache_load, "Load a sound file into the sample cache (args: name, filename)", 3}, - { "load-sample-lazy", pa_cli_command_scache_load, "Lazily load a sound file into the sample cache (args: name, filename)", 3}, - { "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2}, - { "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3}, - { "list-autoload", pa_cli_command_autoload_list, "List autoload entries", 1}, - { "add-autoload-sink", pa_cli_command_autoload_add, "Add autoload entry for a sink (args: sink, module name, arguments)", 4}, - { "add-autoload-source", pa_cli_command_autoload_add, "Add autoload entry for a source (args: source, module name, arguments)", 4}, - { "remove-autoload-sink", pa_cli_command_autoload_remove, "Remove autoload entry for a sink (args: name)", 2}, - { "remove-autoload-source", pa_cli_command_autoload_remove, "Remove autoload entry for a source (args: name)", 2}, - { "dump", pa_cli_command_dump, "Dump daemon configuration", 1}, - { "list-props", pa_cli_command_list_props, NULL, 1}, - { NULL, NULL, NULL, 0 } -}; - -static const char whitespace[] = " \t\n\r"; -static const char linebreak[] = "\n\r"; - -static uint32_t parse_index(const char *n) { - uint32_t idx; - - if (pa_atou(n, &idx) < 0) - return (uint32_t) PA_IDXSET_INVALID; - - return idx; -} - -static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, PA_GCC_UNUSED pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - assert(c && c->mainloop && t); - c->mainloop->quit(c->mainloop, 0); - return 0; -} - -static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - const struct command*command; - assert(c && t && buf); - - pa_strbuf_puts(buf, "Available commands:\n"); - - for (command = commands; command->name; command++) - if (command->help) - pa_strbuf_printf(buf, " %-25s %s\n", command->name, command->help); - return 0; -} - -static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - char *s; - assert(c && t); - s = pa_module_list_to_string(c); - assert(s); - pa_strbuf_puts(buf, s); - pa_xfree(s); - return 0; -} - -static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - char *s; - assert(c && t); - s = pa_client_list_to_string(c); - assert(s); - pa_strbuf_puts(buf, s); - pa_xfree(s); - return 0; -} - -static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - char *s; - assert(c && t); - s = pa_sink_list_to_string(c); - assert(s); - pa_strbuf_puts(buf, s); - pa_xfree(s); - return 0; -} - -static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - char *s; - assert(c && t); - s = pa_source_list_to_string(c); - assert(s); - pa_strbuf_puts(buf, s); - pa_xfree(s); - return 0; -} - -static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - char *s; - assert(c && t); - s = pa_sink_input_list_to_string(c); - assert(s); - pa_strbuf_puts(buf, s); - pa_xfree(s); - return 0; -} - -static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - char *s; - assert(c && t); - s = pa_source_output_list_to_string(c); - assert(s); - pa_strbuf_puts(buf, s); - pa_xfree(s); - return 0; -} - -static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - char s[256]; - assert(c && t); - - pa_bytes_snprint(s, sizeof(s), c->memblock_stat->total_size); - pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n", - c->memblock_stat->total, - s); - - pa_bytes_snprint(s, sizeof(s), c->memblock_stat->allocated_size); - pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n", - c->memblock_stat->allocated, - s); - - pa_bytes_snprint(s, sizeof(s), pa_scache_total_size(c)); - pa_strbuf_printf(buf, "Total sample cache size: %s.\n", s); - - pa_sample_spec_snprint(s, sizeof(s), &c->default_sample_spec); - pa_strbuf_printf(buf, "Default sample spec: %s\n", s); - - pa_strbuf_printf(buf, "Default sink name: %s\n" - "Default source name: %s\n", - pa_namereg_get_default_sink_name(c), - pa_namereg_get_default_source_name(c)); - - return 0; -} - -static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { - assert(c && t); - pa_cli_command_stat(c, t, buf, fail); - pa_cli_command_modules(c, t, buf, fail); - pa_cli_command_sinks(c, t, buf, fail); - pa_cli_command_sources(c, t, buf, fail); - pa_cli_command_clients(c, t, buf, fail); - pa_cli_command_sink_inputs(c, t, buf, fail); - pa_cli_command_source_outputs(c, t, buf, fail); - pa_cli_command_scache_list(c, t, buf, fail); - pa_cli_command_autoload_list(c, t, buf, fail); - return 0; -} - -static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - pa_module *m; - const char *name; - assert(c && t); - - if (!(name = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n"); - return -1; - } - - if (!(m = pa_module_load(c, name, pa_tokenizer_get(t, 2)))) { - pa_strbuf_puts(buf, "Module load failed.\n"); - return -1; - } - - return 0; -} - -static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - pa_module *m; - uint32_t idx; - const char *i; - char *e; - assert(c && t); - - if (!(i = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify the module index.\n"); - return -1; - } - - idx = (uint32_t) strtoul(i, &e, 10); - if (*e || !(m = pa_idxset_get_by_index(c->modules, idx))) { - pa_strbuf_puts(buf, "Invalid module index.\n"); - return -1; - } - - pa_module_unload_request(m); - return 0; -} - -static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - const char *n, *v; - pa_sink *sink; - uint32_t volume; - pa_cvolume cvolume; - - if (!(n = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n"); - return -1; - } - - if (!(v = pa_tokenizer_get(t, 2))) { - pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); - return -1; - } - - if (pa_atou(v, &volume) < 0) { - pa_strbuf_puts(buf, "Failed to parse volume.\n"); - return -1; - } - - if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) { - pa_strbuf_puts(buf, "No sink found by this name or index.\n"); - return -1; - } - - pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume); - pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &cvolume); - return 0; -} - -static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - const char *n, *v; - pa_sink_input *si; - pa_volume_t volume; - pa_cvolume cvolume; - uint32_t idx; - - if (!(n = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n"); - return -1; - } - - if ((idx = parse_index(n)) == PA_IDXSET_INVALID) { - pa_strbuf_puts(buf, "Failed to parse index.\n"); - return -1; - } - - if (!(v = pa_tokenizer_get(t, 2))) { - pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); - return -1; - } - - if (pa_atou(v, &volume) < 0) { - pa_strbuf_puts(buf, "Failed to parse volume.\n"); - return -1; - } - - if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) { - pa_strbuf_puts(buf, "No sink input found with this index.\n"); - return -1; - } - - pa_cvolume_set(&cvolume, si->sample_spec.channels, volume); - pa_sink_input_set_volume(si, &cvolume); - return 0; -} - -static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - const char *n; - assert(c && t); - - if (!(n = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n"); - return -1; - } - - pa_namereg_set_default(c, n, PA_NAMEREG_SINK); - return 0; -} - -static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - const char *n; - assert(c && t); - - if (!(n = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n"); - return -1; - } - - pa_namereg_set_default(c, n, PA_NAMEREG_SOURCE); - return 0; -} - -static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - const char *n; - pa_client *client; - uint32_t idx; - assert(c && t); - - if (!(n = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a client by its index.\n"); - return -1; - } - - if ((idx = parse_index(n)) == PA_IDXSET_INVALID) { - pa_strbuf_puts(buf, "Failed to parse index.\n"); - return -1; - } - - if (!(client = pa_idxset_get_by_index(c->clients, idx))) { - pa_strbuf_puts(buf, "No client found by this index.\n"); - return -1; - } - - pa_client_kill(client); - return 0; -} - -static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - const char *n; - pa_sink_input *sink_input; - uint32_t idx; - assert(c && t); - - if (!(n = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n"); - return -1; - } - - if ((idx = parse_index(n)) == PA_IDXSET_INVALID) { - pa_strbuf_puts(buf, "Failed to parse index.\n"); - return -1; - } - - if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx))) { - pa_strbuf_puts(buf, "No sink input found by this index.\n"); - return -1; - } - - pa_sink_input_kill(sink_input); - return 0; -} - -static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - const char *n; - pa_source_output *source_output; - uint32_t idx; - assert(c && t); - - if (!(n = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a source output by its index.\n"); - return -1; - } - - if ((idx = parse_index(n)) == PA_IDXSET_INVALID) { - pa_strbuf_puts(buf, "Failed to parse index.\n"); - return -1; - } - - if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx))) { - pa_strbuf_puts(buf, "No source output found by this index.\n"); - return -1; - } - - pa_source_output_kill(source_output); - return 0; -} - -static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - char *s; - assert(c && t); - s = pa_scache_list_to_string(c); - assert(s); - pa_strbuf_puts(buf, s); - pa_xfree(s); - return 0; -} - -static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { - const char *n, *sink_name; - pa_sink *sink; - assert(c && t && buf && fail); - - if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) { - pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n"); - return -1; - } - - if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) { - pa_strbuf_puts(buf, "No sink by that name.\n"); - return -1; - } - - if (pa_scache_play_item(c, n, sink, NULL) < 0) { - pa_strbuf_puts(buf, "Failed to play sample.\n"); - return -1; - } - - return 0; -} - -static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { - const char *n; - assert(c && t && buf && fail); - - if (!(n = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a sample name.\n"); - return -1; - } - - if (pa_scache_remove_item(c, n) < 0) { - pa_strbuf_puts(buf, "Failed to remove sample.\n"); - return -1; - } - - return 0; -} - -static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { - const char *fname, *n; - int r; - assert(c && t && buf && fail); - - if (!(fname = pa_tokenizer_get(t, 2)) || !(n = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n"); - return -1; - } - - if (strstr(pa_tokenizer_get(t, 0), "lazy")) - r = pa_scache_add_file_lazy(c, n, fname, NULL); - else - r = pa_scache_add_file(c, n, fname, NULL); - - if (r < 0) - pa_strbuf_puts(buf, "Failed to load sound file.\n"); - - return 0; -} - -static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { - const char *pname; - assert(c && t && buf && fail); - - if (!(pname = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a path name.\n"); - return -1; - } - - if (pa_scache_add_directory_lazy(c, pname) < 0) { - pa_strbuf_puts(buf, "Failed to load directory.\n"); - return -1; - } - - return 0; -} - -static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { - const char *fname, *sink_name; - pa_sink *sink; - assert(c && t && buf && fail); - - if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) { - pa_strbuf_puts(buf, "You need to specify a file name and a sink name.\n"); - return -1; - } - - if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) { - pa_strbuf_puts(buf, "No sink by that name.\n"); - return -1; - } - - - return pa_play_file(sink, fname, NULL); -} - -static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { - const char *a, *b; - assert(c && t && buf && fail); - - if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) { - pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n"); - return -1; - } - - pa_autoload_add(c, a, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, b, pa_tokenizer_get(t, 3), NULL); - - return 0; -} - -static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { - const char *name; - assert(c && t && buf && fail); - - if (!(name = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a device name\n"); - return -1; - } - - if (pa_autoload_remove_by_name(c, name, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE) < 0) { - pa_strbuf_puts(buf, "Failed to remove autload entry\n"); - return -1; - } - - return 0; -} - -static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - char *s; - assert(c && t); - s = pa_autoload_list_to_string(c); - assert(s); - pa_strbuf_puts(buf, s); - pa_xfree(s); - return 0; -} - -static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - assert(c && t); - pa_property_dump(c, buf); - return 0; -} - -static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { - pa_module *m; - pa_sink *s; - int nl; - const char *p; - uint32_t idx; - char txt[256]; - time_t now; - void *i; - pa_autoload_entry *a; - - assert(c && t); - - time(&now); - -#ifdef HAVE_CTIME_R - pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime_r(&now, txt)); -#else - pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now)); -#endif - - - for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) { - if (m->auto_unload) - continue; - - pa_strbuf_printf(buf, "load-module %s", m->name); - - if (m->argument) - pa_strbuf_printf(buf, " %s", m->argument); - - pa_strbuf_puts(buf, "\n"); - } - - nl = 0; - - for (s = pa_idxset_first(c->sinks, &idx); s; s = pa_idxset_next(c->sinks, &idx)) { - if (s->owner && s->owner->auto_unload) - continue; - - if (!nl) { - pa_strbuf_puts(buf, "\n"); - nl = 1; - } - - pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", s->name, pa_cvolume_avg(pa_sink_get_volume(s, PA_MIXER_HARDWARE))); - } - - - if (c->autoload_hashmap) { - nl = 0; - - i = NULL; - while ((a = pa_hashmap_iterate(c->autoload_hashmap, &i, NULL))) { - - if (!nl) { - pa_strbuf_puts(buf, "\n"); - nl = 1; - } - - pa_strbuf_printf(buf, "add-autoload-%s %s %s", a->type == PA_NAMEREG_SINK ? "sink" : "source", a->name, a->module); - - if (a->argument) - pa_strbuf_printf(buf, " %s", a->argument); - - pa_strbuf_puts(buf, "\n"); - } - } - - nl = 0; - - if ((p = pa_namereg_get_default_sink_name(c))) { - if (!nl) { - pa_strbuf_puts(buf, "\n"); - nl = 1; - } - pa_strbuf_printf(buf, "set-default-sink %s\n", p); - } - - if ((p = pa_namereg_get_default_source_name(c))) { - if (!nl) { - pa_strbuf_puts(buf, "\n"); - nl = 1; - } - pa_strbuf_printf(buf, "set-default-source %s\n", p); - } - - pa_strbuf_puts(buf, "\n### EOF\n"); - - return 0; -} - - -int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail) { - const char *cs; - - cs = s+strspn(s, whitespace); - - if (*cs == '#' || !*cs) - return 0; - else if (*cs == '.') { - if (!strcmp(cs, FAIL_META)) - *fail = 1; - else if (!strcmp(cs, NOFAIL_META)) - *fail = 0; - else { - size_t l; - l = strcspn(cs, whitespace); - - if (l == sizeof(INCLUDE_META)-1 && !strncmp(cs, INCLUDE_META, l)) { - const char *filename = cs+l+strspn(cs+l, whitespace); - - if (pa_cli_command_execute_file(c, filename, buf, fail) < 0) - if (*fail) return -1; - } else { - pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs); - if (*fail) return -1; - } - } - } else { - const struct command*command; - int unknown = 1; - size_t l; - - l = strcspn(cs, whitespace); - - for (command = commands; command->name; command++) - if (strlen(command->name) == l && !strncmp(cs, command->name, l)) { - int ret; - pa_tokenizer *t = pa_tokenizer_new(cs, command->args); - assert(t); - ret = command->proc(c, t, buf, fail); - pa_tokenizer_free(t); - unknown = 0; - - if (ret < 0 && *fail) - return -1; - - break; - } - - if (unknown) { - pa_strbuf_printf(buf, "Unknown command: %s\n", cs); - if (*fail) - return -1; - } - } - - return 0; -} - -int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail) { - char line[256]; - FILE *f = NULL; - int ret = -1; - assert(c && fn && buf); - - if (!(f = fopen(fn, "r"))) { - pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, strerror(errno)); - if (!*fail) - ret = 0; - goto fail; - } - - while (fgets(line, sizeof(line), f)) { - char *e = line + strcspn(line, linebreak); - *e = 0; - - if (pa_cli_command_execute_line(c, line, buf, fail) < 0 && *fail) - goto fail; - } - - ret = 0; - -fail: - if (f) - fclose(f); - - return ret; -} - -int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail) { - const char *p; - assert(c && s && buf && fail); - - p = s; - while (*p) { - size_t l = strcspn(p, linebreak); - char *line = pa_xstrndup(p, l); - - if (pa_cli_command_execute_line(c, line, buf, fail) < 0&& *fail) { - pa_xfree(line); - return -1; - } - pa_xfree(line); - - p += l; - p += strspn(p, linebreak); - } - - return 0; -} diff --git a/polyp/cli-command.h b/polyp/cli-command.h deleted file mode 100644 index 78b8d5c6..00000000 --- a/polyp/cli-command.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef fooclicommandhfoo -#define fooclicommandhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "strbuf.h" -#include "core.h" - -/* Execute a single CLI command. Write the results to the string - * buffer *buf. If *fail is non-zero the function will return -1 when - * one or more of the executed commands failed. *fail - * may be modified by the function call. */ -int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail); - -/* Execute a whole file of CLI commands */ -int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail); - -/* Split the specified string into lines and run pa_cli_command_execute_line() for each. */ -int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail); - -#endif diff --git a/polyp/cli-text.c b/polyp/cli-text.c deleted file mode 100644 index 328aca4c..00000000 --- a/polyp/cli-text.c +++ /dev/null @@ -1,385 +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 -#endif - -#include -#include - -#include "cli-text.h" -#include "module.h" -#include "client.h" -#include "sink.h" -#include "source.h" -#include "sink-input.h" -#include "source-output.h" -#include "strbuf.h" -#include "sample-util.h" -#include "scache.h" -#include "autoload.h" -#include "xmalloc.h" -#include "volume.h" - -char *pa_module_list_to_string(pa_core *c) { - pa_strbuf *s; - pa_module *m; - uint32_t idx = PA_IDXSET_INVALID; - assert(c); - - s = pa_strbuf_new(); - assert(s); - - pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(c->modules)); - - for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) - pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\targument: <%s>\n\tused: %i\n\tauto unload: %s\n", m->index, m->name, m->argument, m->n_used, m->auto_unload ? "yes" : "no"); - - return pa_strbuf_tostring_free(s); -} - -char *pa_client_list_to_string(pa_core *c) { - pa_strbuf *s; - pa_client *client; - uint32_t idx = PA_IDXSET_INVALID; - assert(c); - - s = pa_strbuf_new(); - assert(s); - - pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients)); - - for (client = pa_idxset_first(c->clients, &idx); client; client = pa_idxset_next(c->clients, &idx)) { - pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\tdriver: <%s>\n", client->index, client->name, client->driver); - - if (client->owner) - pa_strbuf_printf(s, "\towner module: <%u>\n", client->owner->index); - } - - return pa_strbuf_tostring_free(s); -} - -char *pa_sink_list_to_string(pa_core *c) { - pa_strbuf *s; - pa_sink *sink; - uint32_t idx = PA_IDXSET_INVALID; - assert(c); - - s = pa_strbuf_new(); - assert(s); - - pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks)); - - for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) { - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; - - pa_strbuf_printf( - s, - " %c index: %u\n" - "\tname: <%s>\n" - "\tdriver: <%s>\n" - "\tvolume: <%s>\n" - "\tlatency: <%0.0f usec>\n" - "\tmonitor_source: <%u>\n" - "\tsample spec: <%s>\n" - "\tchannel map: <%s>\n", - c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ', - sink->index, sink->name, - sink->driver, - pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, PA_MIXER_HARDWARE)), - (double) pa_sink_get_latency(sink), - sink->monitor_source->index, - pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec), - pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map)); - - if (sink->owner) - pa_strbuf_printf(s, "\towner module: <%u>\n", sink->owner->index); - if (sink->description) - pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description); - } - - return pa_strbuf_tostring_free(s); -} - -char *pa_source_list_to_string(pa_core *c) { - pa_strbuf *s; - pa_source *source; - uint32_t idx = PA_IDXSET_INVALID; - assert(c); - - s = pa_strbuf_new(); - assert(s); - - pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources)); - - for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) { - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; - - - pa_strbuf_printf( - s, - " %c index: %u\n" - "\tname: <%s>\n" - "\tdriver: <%s>\n" - "\tlatency: <%0.0f usec>\n" - "\tsample spec: <%s>\n" - "\tchannel map: <%s>\n", - c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ', - source->index, - source->name, - source->driver, - (double) pa_source_get_latency(source), - pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec), - pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map)); - - if (source->monitor_of) - pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index); - if (source->owner) - pa_strbuf_printf(s, "\towner module: <%u>\n", source->owner->index); - if (source->description) - pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description); - } - - return pa_strbuf_tostring_free(s); -} - - -char *pa_source_output_list_to_string(pa_core *c) { - pa_strbuf *s; - pa_source_output *o; - uint32_t idx = PA_IDXSET_INVALID; - static const char* const state_table[] = { - "RUNNING", - "CORKED", - "DISCONNECTED" - }; - assert(c); - - s = pa_strbuf_new(); - assert(s); - - pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs)); - - for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) { - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; - - assert(o->source); - - pa_strbuf_printf( - s, - " index: %u\n" - "\tname: '%s'\n" - "\tdriver: <%s>\n" - "\tstate: %s\n" - "\tsource: <%u> '%s'\n" - "\tsample spec: <%s>\n" - "\tchannel map: <%s>\n" - "\tresample method: %s\n", - o->index, - o->name, - o->driver, - state_table[o->state], - o->source->index, o->source->name, - pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec), - pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map), - pa_resample_method_to_string(pa_source_output_get_resample_method(o))); - if (o->owner) - pa_strbuf_printf(s, "\towner module: <%u>\n", o->owner->index); - if (o->client) - pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", o->client->index, o->client->name); - } - - return pa_strbuf_tostring_free(s); -} - -char *pa_sink_input_list_to_string(pa_core *c) { - pa_strbuf *s; - pa_sink_input *i; - uint32_t idx = PA_IDXSET_INVALID; - static const char* const state_table[] = { - "RUNNING", - "CORKED", - "DISCONNECTED" - }; - - assert(c); - s = pa_strbuf_new(); - assert(s); - - pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs)); - - for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) { - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; - - assert(i->sink); - - pa_strbuf_printf( - s, - " index: %u\n" - "\tname: <%s>\n" - "\tdriver: <%s>\n" - "\tstate: %s\n" - "\tsink: <%u> '%s'\n" - "\tvolume: <%s>\n" - "\tlatency: <%0.0f usec>\n" - "\tsample spec: <%s>\n" - "\tchannel map: <%s>\n" - "\tresample method: %s\n", - i->index, - i->name, - i->driver, - state_table[i->state], - i->sink->index, i->sink->name, - pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)), - (double) pa_sink_input_get_latency(i), - pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec), - pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), - pa_resample_method_to_string(pa_sink_input_get_resample_method(i))); - - if (i->owner) - pa_strbuf_printf(s, "\towner module: <%u>\n", i->owner->index); - if (i->client) - pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", i->client->index, i->client->name); - } - - return pa_strbuf_tostring_free(s); -} - -char *pa_scache_list_to_string(pa_core *c) { - pa_strbuf *s; - assert(c); - - s = pa_strbuf_new(); - assert(s); - - pa_strbuf_printf(s, "%u cache entries available.\n", c->scache ? pa_idxset_size(c->scache) : 0); - - if (c->scache) { - pa_scache_entry *e; - uint32_t idx = PA_IDXSET_INVALID; - - for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) { - double l = 0; - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; - - if (e->memchunk.memblock) { - pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec); - pa_channel_map_snprint(cm, sizeof(cm), &e->channel_map); - l = (double) e->memchunk.length / pa_bytes_per_second(&e->sample_spec); - } - - pa_strbuf_printf( - s, - " name: <%s>\n" - "\tindex: <%u>\n" - "\tsample spec: <%s>\n" - "\tchannel map: <%s>\n" - "\tlength: <%u>\n" - "\tduration: <%0.1fs>\n" - "\tvolume: <%s>\n" - "\tlazy: %s\n" - "\tfilename: %s\n", - e->name, - e->index, - ss, - cm, - e->memchunk.memblock ? e->memchunk.length : 0, - l, - pa_cvolume_snprint(cv, sizeof(cv), &e->volume), - e->lazy ? "yes" : "no", - e->filename ? e->filename : "n/a"); - } - } - - return pa_strbuf_tostring_free(s); -} - -char *pa_autoload_list_to_string(pa_core *c) { - pa_strbuf *s; - assert(c); - - s = pa_strbuf_new(); - assert(s); - - pa_strbuf_printf(s, "%u autoload entries available.\n", c->autoload_hashmap ? pa_hashmap_size(c->autoload_hashmap) : 0); - - if (c->autoload_hashmap) { - pa_autoload_entry *e; - void *state = NULL; - - while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) { - pa_strbuf_printf( - s, " name: <%s>\n\ttype: <%s>\n\tindex: <%u>\n\tmodule_name: <%s>\n\targuments: <%s>\n", - e->name, - e->type == PA_NAMEREG_SOURCE ? "source" : "sink", - e->index, - e->module, - e->argument); - - } - } - - return pa_strbuf_tostring_free(s); -} - -char *pa_full_status_string(pa_core *c) { - pa_strbuf *s; - int i; - - s = pa_strbuf_new(); - - for (i = 0; i < 8; i++) { - char *t = NULL; - - switch (i) { - case 0: - t = pa_sink_list_to_string(c); - break; - case 1: - t = pa_source_list_to_string(c); - break; - case 2: - t = pa_sink_input_list_to_string(c); - break; - case 3: - t = pa_source_output_list_to_string(c); - break; - case 4: - t = pa_client_list_to_string(c); - break; - case 5: - t = pa_module_list_to_string(c); - break; - case 6: - t = pa_scache_list_to_string(c); - break; - case 7: - t = pa_autoload_list_to_string(c); - break; - } - - pa_strbuf_puts(s, t); - pa_xfree(t); - } - - return pa_strbuf_tostring_free(s); -} diff --git a/polyp/cli-text.h b/polyp/cli-text.h deleted file mode 100644 index 7a1a0361..00000000 --- a/polyp/cli-text.h +++ /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(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/polyp/cli.c b/polyp/cli.c deleted file mode 100644 index bc0c285d..00000000 --- a/polyp/cli.c +++ /dev/null @@ -1,147 +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 -#endif - -#include -#include -#include -#include - -#include "ioline.h" -#include "cli.h" -#include "module.h" -#include "sink.h" -#include "source.h" -#include "client.h" -#include "sink-input.h" -#include "source-output.h" -#include "tokenizer.h" -#include "strbuf.h" -#include "namereg.h" -#include "cli-text.h" -#include "cli-command.h" -#include "xmalloc.h" -#include "log.h" - -#define PROMPT ">>> " - -struct pa_cli { - pa_core *core; - pa_ioline *line; - - void (*eof_callback)(pa_cli *c, void *userdata); - void *userdata; - - pa_client *client; - - int fail, kill_requested, defer_kill; -}; - -static void line_callback(pa_ioline *line, const char *s, void *userdata); -static void client_kill(pa_client *c); - -pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) { - char cname[256]; - pa_cli *c; - assert(io); - - c = pa_xmalloc(sizeof(pa_cli)); - c->core = core; - c->line = pa_ioline_new(io); - assert(c->line); - - c->userdata = NULL; - c->eof_callback = NULL; - - pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); - c->client = pa_client_new(core, __FILE__, cname); - assert(c->client); - c->client->kill = client_kill; - c->client->userdata = c; - c->client->owner = m; - - pa_ioline_set_callback(c->line, line_callback, c); - pa_ioline_puts(c->line, "Welcome to polypaudio! Use \"help\" for usage information.\n"PROMPT); - - c->fail = c->kill_requested = c->defer_kill = 0; - - return c; -} - -void pa_cli_free(pa_cli *c) { - assert(c); - pa_ioline_close(c->line); - pa_ioline_unref(c->line); - pa_client_free(c->client); - pa_xfree(c); -} - -static void client_kill(pa_client *client) { - pa_cli *c; - assert(client && client->userdata); - c = client->userdata; - - pa_log_debug(__FILE__": CLI client killed.\n"); - if (c->defer_kill) - c->kill_requested = 1; - else { - if (c->eof_callback) - c->eof_callback(c, c->userdata); - } -} - -static void line_callback(pa_ioline *line, const char *s, void *userdata) { - pa_strbuf *buf; - pa_cli *c = userdata; - char *p; - assert(line && c); - - if (!s) { - pa_log_debug(__FILE__": CLI got EOF from user.\n"); - if (c->eof_callback) - c->eof_callback(c, c->userdata); - - return; - } - - buf = pa_strbuf_new(); - assert(buf); - c->defer_kill++; - pa_cli_command_execute_line(c->core, s, buf, &c->fail); - c->defer_kill--; - pa_ioline_puts(line, p = pa_strbuf_tostring_free(buf)); - pa_xfree(p); - - if (c->kill_requested) { - if (c->eof_callback) - c->eof_callback(c, c->userdata); - } else - pa_ioline_puts(line, PROMPT); -} - -void pa_cli_set_eof_callback(pa_cli *c, void (*cb)(pa_cli*c, void *userdata), void *userdata) { - assert(c); - c->eof_callback = cb; - c->userdata = userdata; -} diff --git a/polyp/cli.h b/polyp/cli.h deleted file mode 100644 index 03f31c22..00000000 --- a/polyp/cli.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef fooclihfoo -#define fooclihfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "iochannel.h" -#include "core.h" -#include "module.h" - -typedef struct pa_cli pa_cli; - -/* Create a new command line session on the specified io channel owned by the specified module */ -pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m); -void pa_cli_free(pa_cli *cli); - -/* Set a callback function that is called whenever the command line session is terminated */ -void pa_cli_set_eof_callback(pa_cli *cli, void (*cb)(pa_cli*c, void *userdata), void *userdata); - -#endif diff --git a/polyp/client-conf-x11.c b/polyp/client-conf-x11.c deleted file mode 100644 index 4fd75744..00000000 --- a/polyp/client-conf-x11.c +++ /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-13071 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include -#include - -#include "client-conf-x11.h" -#include "x11prop.h" -#include "log.h" -#include "xmalloc.h" -#include "util.h" - -int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) { - Display *d = NULL; - int ret = -1; - char t[1024]; - - if (!dname && !getenv("DISPLAY")) - goto finish; - - if (!(d = XOpenDisplay(dname))) { - pa_log(__FILE__": XOpenDisplay() failed\n"); - goto finish; - } - - if (pa_x11_get_prop(d, "POLYP_SERVER", t, sizeof(t))) { - pa_xfree(c->default_server); - c->default_server = pa_xstrdup(t); - } - - if (pa_x11_get_prop(d, "POLYP_SINK", t, sizeof(t))) { - pa_xfree(c->default_sink); - c->default_sink = pa_xstrdup(t); - } - - if (pa_x11_get_prop(d, "POLYP_SOURCE", t, sizeof(t))) { - pa_xfree(c->default_source); - c->default_source = pa_xstrdup(t); - } - - if (pa_x11_get_prop(d, "POLYP_COOKIE", t, sizeof(t))) { - uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; - - if (pa_parsehex(t, cookie, sizeof(cookie)) != sizeof(cookie)) { - pa_log(__FILE__": failed to parse cookie data\n"); - goto finish; - } - - assert(sizeof(cookie) == sizeof(c->cookie)); - memcpy(c->cookie, cookie, sizeof(cookie)); - - c->cookie_valid = 1; - - pa_xfree(c->cookie_file); - c->cookie_file = NULL; - } - - ret = 0; - -finish: - if (d) - XCloseDisplay(d); - - return ret; - -} diff --git a/polyp/client-conf-x11.h b/polyp/client-conf-x11.h deleted file mode 100644 index 64459224..00000000 --- a/polyp/client-conf-x11.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef fooclientconfx11hfoo -#define fooclientconfx11hfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "client-conf.h" - -/* Load client configuration data from the specified X11 display, - * overwriting the current settings in *c */ -int pa_client_conf_from_x11(pa_client_conf *c, const char *display); - -#endif diff --git a/polyp/client-conf.c b/polyp/client-conf.c deleted file mode 100644 index bcd4275d..00000000 --- a/polyp/client-conf.c +++ /dev/null @@ -1,190 +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 -#endif - -#include -#include -#include -#include -#include - -#include "client-conf.h" -#include "xmalloc.h" -#include "log.h" -#include "conf-parser.h" -#include "util.h" -#include "authkey.h" - -#ifndef DEFAULT_CONFIG_DIR -# ifndef OS_IS_WIN32 -# define DEFAULT_CONFIG_DIR "/etc/polypaudio" -# else -# define DEFAULT_CONFIG_DIR "%POLYP_ROOT%" -# endif -#endif - -#ifndef OS_IS_WIN32 -# define PATH_SEP "/" -#else -# define PATH_SEP "\\" -#endif - -#define DEFAULT_CLIENT_CONFIG_FILE DEFAULT_CONFIG_DIR PATH_SEP "client.conf" -#define DEFAULT_CLIENT_CONFIG_FILE_USER ".polypaudio" PATH_SEP "client.conf" - -#define ENV_CLIENT_CONFIG_FILE "POLYP_CLIENTCONFIG" -#define ENV_DEFAULT_SINK "POLYP_SINK" -#define ENV_DEFAULT_SOURCE "POLYP_SOURCE" -#define ENV_DEFAULT_SERVER "POLYP_SERVER" -#define ENV_DAEMON_BINARY "POLYP_BINARY" -#define ENV_COOKIE_FILE "POLYP_COOKIE" - -static const pa_client_conf default_conf = { - .daemon_binary = NULL, - .extra_arguments = NULL, - .default_sink = NULL, - .default_source = NULL, - .default_server = NULL, - .autospawn = 0, - .cookie_file = NULL, - .cookie_valid = 0 -}; - -pa_client_conf *pa_client_conf_new(void) { - pa_client_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf)); - - c->daemon_binary = pa_xstrdup(POLYPAUDIO_BINARY); - c->extra_arguments = pa_xstrdup("--log-target=syslog --exit-idle-time=5"); - c->cookie_file = pa_xstrdup(PA_NATIVE_COOKIE_FILE); - - return c; -} - -void pa_client_conf_free(pa_client_conf *c) { - assert(c); - pa_xfree(c->daemon_binary); - pa_xfree(c->extra_arguments); - pa_xfree(c->default_sink); - pa_xfree(c->default_source); - pa_xfree(c->default_server); - pa_xfree(c->cookie_file); - pa_xfree(c); -} -int pa_client_conf_load(pa_client_conf *c, const char *filename) { - FILE *f = NULL; - char *fn = NULL; - int r = -1; - - /* Prepare the configuration parse table */ - pa_config_item table[] = { - { "daemon-binary", pa_config_parse_string, NULL }, - { "extra-arguments", pa_config_parse_string, NULL }, - { "default-sink", pa_config_parse_string, NULL }, - { "default-source", pa_config_parse_string, NULL }, - { "default-server", pa_config_parse_string, NULL }, - { "autospawn", pa_config_parse_bool, NULL }, - { "cookie-file", pa_config_parse_string, NULL }, - { NULL, NULL, NULL }, - }; - - table[0].data = &c->daemon_binary; - table[1].data = &c->extra_arguments; - table[2].data = &c->default_sink; - table[3].data = &c->default_source; - table[4].data = &c->default_server; - table[5].data = &c->autospawn; - table[6].data = &c->cookie_file; - - f = filename ? - fopen((fn = pa_xstrdup(filename)), "r") : - pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn); - - if (!f && errno != EINTR) { - pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno)); - goto finish; - } - - r = f ? pa_config_parse(fn, f, table, NULL) : 0; - - if (!r) - r = pa_client_conf_load_cookie(c); - - -finish: - pa_xfree(fn); - - if (f) - fclose(f); - - return r; -} - -int pa_client_conf_env(pa_client_conf *c) { - char *e; - - if ((e = getenv(ENV_DEFAULT_SINK))) { - pa_xfree(c->default_sink); - c->default_sink = pa_xstrdup(e); - } - - if ((e = getenv(ENV_DEFAULT_SOURCE))) { - pa_xfree(c->default_source); - c->default_source = pa_xstrdup(e); - } - - if ((e = getenv(ENV_DEFAULT_SERVER))) { - pa_xfree(c->default_server); - c->default_server = pa_xstrdup(e); - } - - if ((e = getenv(ENV_DAEMON_BINARY))) { - pa_xfree(c->daemon_binary); - c->daemon_binary = pa_xstrdup(e); - } - - if ((e = getenv(ENV_COOKIE_FILE))) { - pa_xfree(c->cookie_file); - c->cookie_file = pa_xstrdup(e); - - return pa_client_conf_load_cookie(c); - } - - return 0; -} - -int pa_client_conf_load_cookie(pa_client_conf* c) { - assert(c); - - c->cookie_valid = 0; - - if (!c->cookie_file) - return -1; - - if (pa_authkey_load_auto(c->cookie_file, c->cookie, sizeof(c->cookie)) < 0) - return -1; - - c->cookie_valid = 1; - return 0; -} - diff --git a/polyp/client-conf.h b/polyp/client-conf.h deleted file mode 100644 index 7ca2f233..00000000 --- a/polyp/client-conf.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef fooclientconfhfoo -#define fooclientconfhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "native-common.h" - -/* A structure containing configuration data for polypaudio clients. */ - -typedef struct pa_client_conf { - char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file; - int autospawn; - uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; - int cookie_valid; /* non-zero, when cookie is valid */ -} pa_client_conf; - -/* Create a new configuration data object and reset it to defaults */ -pa_client_conf *pa_client_conf_new(void); -void pa_client_conf_free(pa_client_conf *c); - -/* Load the configuration data from the speicified file, overwriting - * the current settings in *c. When the filename is NULL, the - * default client configuration file name is used. */ -int pa_client_conf_load(pa_client_conf *c, const char *filename); - -/* Load the configuration data from the environment of the current - process, overwriting the current settings in *c. */ -int pa_client_conf_env(pa_client_conf *c); - -/* Load cookie data from c->cookie_file into c->cookie */ -int pa_client_conf_load_cookie(pa_client_conf* c); - -#endif diff --git a/polyp/client.c b/polyp/client.c deleted file mode 100644 index 3c2084bf..00000000 --- a/polyp/client.c +++ /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 -#endif - -#include -#include -#include -#include - -#include "client.h" -#include "xmalloc.h" -#include "subscribe.h" -#include "log.h" - -pa_client *pa_client_new(pa_core *core, const char *name, const char *driver) { - pa_client *c; - int r; - assert(core); - - c = pa_xmalloc(sizeof(pa_client)); - c->name = pa_xstrdup(name); - c->driver = pa_xstrdup(driver); - c->owner = NULL; - c->core = core; - - c->kill = NULL; - c->userdata = NULL; - - r = pa_idxset_put(core->clients, c, &c->index); - assert(c->index != PA_IDXSET_INVALID && r >= 0); - - pa_log_info(__FILE__": created %u \"%s\"\n", c->index, c->name); - pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index); - - pa_core_check_quit(core); - - return c; -} - -void pa_client_free(pa_client *c) { - assert(c && c->core); - - pa_idxset_remove_by_data(c->core->clients, c, NULL); - - pa_core_check_quit(c->core); - - pa_log_info(__FILE__": freed %u \"%s\"\n", c->index, c->name); - pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index); - pa_xfree(c->name); - pa_xfree(c->driver); - pa_xfree(c); -} - -void pa_client_kill(pa_client *c) { - assert(c); - if (!c->kill) { - pa_log_warn(__FILE__": kill() operation not implemented for client %u\n", c->index); - return; - } - - c->kill(c); -} - -void pa_client_set_name(pa_client *c, const char *name) { - assert(c); - pa_xfree(c->name); - c->name = pa_xstrdup(name); - - pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index); -} diff --git a/polyp/client.conf.in b/polyp/client.conf.in deleted file mode 100644 index fbf645a4..00000000 --- a/polyp/client.conf.in +++ /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/client.h b/polyp/client.h deleted file mode 100644 index 92430338..00000000 --- a/polyp/client.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef fooclienthfoo -#define fooclienthfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "core.h" -#include "module.h" - -/* Every connection to the server should have a pa_client - * attached. That way the user may generate a listing of all connected - * clients easily and kill them if he wants.*/ - -typedef struct pa_client pa_client; - -struct pa_client { - uint32_t index; - - pa_module *owner; - char *name, *driver; - pa_core *core; - - void (*kill)(pa_client *c); - void *userdata; -}; - -pa_client *pa_client_new(pa_core *c, const char *name, const char *driver); - -/* This function should be called only by the code that created the client */ -void pa_client_free(pa_client *c); - -/* Code that didn't create the client should call this function to - * request destruction of the client */ -void pa_client_kill(pa_client *c); - -/* Rename the client */ -void pa_client_set_name(pa_client *c, const char *name); - -#endif diff --git a/polyp/cmdline.c b/polyp/cmdline.c deleted file mode 100644 index 5635707f..00000000 --- a/polyp/cmdline.c +++ /dev/null @@ -1,300 +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 -#endif - -#include -#include -#include -#include -#include -#include - -#include "cmdline.h" -#include "util.h" -#include "strbuf.h" -#include "xmalloc.h" - -/* Argument codes for getopt_long() */ -enum { - ARG_HELP = 256, - ARG_VERSION, - ARG_DUMP_CONF, - ARG_DUMP_MODULES, - ARG_DAEMONIZE, - ARG_FAIL, - ARG_LOG_LEVEL, - ARG_HIGH_PRIORITY, - ARG_DISALLOW_MODULE_LOADING, - ARG_EXIT_IDLE_TIME, - ARG_MODULE_IDLE_TIME, - ARG_SCACHE_IDLE_TIME, - ARG_LOG_TARGET, - ARG_LOAD, - ARG_FILE, - ARG_DL_SEARCH_PATH, - ARG_RESAMPLE_METHOD, - ARG_KILL, - ARG_USE_PID_FILE, - ARG_CHECK -}; - -/* Tabel for getopt_long() */ -static struct option long_options[] = { - {"help", 0, 0, ARG_HELP}, - {"version", 0, 0, ARG_VERSION}, - {"dump-conf", 0, 0, ARG_DUMP_CONF}, - {"dump-modules", 0, 0, ARG_DUMP_MODULES}, - {"daemonize", 2, 0, ARG_DAEMONIZE}, - {"fail", 2, 0, ARG_FAIL}, - {"verbose", 2, 0, ARG_LOG_LEVEL}, - {"log-level", 2, 0, ARG_LOG_LEVEL}, - {"high-priority", 2, 0, ARG_HIGH_PRIORITY}, - {"disallow-module-loading", 2, 0, ARG_DISALLOW_MODULE_LOADING}, - {"exit-idle-time", 2, 0, ARG_EXIT_IDLE_TIME}, - {"module-idle-time", 2, 0, ARG_MODULE_IDLE_TIME}, - {"scache-idle-time", 2, 0, ARG_SCACHE_IDLE_TIME}, - {"log-target", 1, 0, ARG_LOG_TARGET}, - {"load", 1, 0, ARG_LOAD}, - {"file", 1, 0, ARG_FILE}, - {"dl-search-path", 1, 0, ARG_DL_SEARCH_PATH}, - {"resample-method", 1, 0, ARG_RESAMPLE_METHOD}, - {"kill", 0, 0, ARG_KILL}, - {"use-pid-file", 2, 0, ARG_USE_PID_FILE}, - {"check", 0, 0, ARG_CHECK}, - {NULL, 0, 0, 0} -}; - -void pa_cmdline_help(const char *argv0) { - const char *e; - - if ((e = strrchr(argv0, '/'))) - e++; - else - e = argv0; - - printf("%s [options]\n\n" - "COMMANDS:\n" - " -h, --help Show this help\n" - " --version Show version\n" - " --dump-conf Dump default configuration\n" - " --dump-modules Dump list of available modules\n" - " -k --kill Kill a running daemon\n" - " --check Check for a running daemon\n\n" - - "OPTIONS:\n" - " -D, --daemonize[=BOOL] Daemonize after startup\n" - " --fail[=BOOL] Quit when startup fails\n" - " --high-priority[=BOOL] Try to set high process priority\n" - " (only available as root)\n" - " --disallow-module-loading[=BOOL] Disallow module loading after startup\n" - " --exit-idle-time=SECS Terminate the daemon when idle and this\n" - " time passed\n" - " --module-idle-time=SECS Unload autoloaded modules when idle and\n" - " this time passed\n" - " --scache-idle-time=SECS Unload autoloaded samples when idle and\n" - " this time passed\n" - " --log-level[=LEVEL] Increase or set verbosity level\n" - " -v Increase the verbosity level\n" - " --log-target={auto,syslog,stderr} Specify the log target\n" - " -p, --dl-search-path=PATH Set the search path for dynamic shared\n" - " objects (plugins)\n" - " --resample-method=[METHOD] Use the specified resampling method\n" - " (one of src-sinc-medium-quality,\n" - " src-sinc-best-quality,src-sinc-fastest\n" - " src-zero-order-hold,src-linear,trivial)\n" - " --use-pid-file[=BOOL] Create a PID file\n\n" - - "STARTUP SCRIPT:\n" - " -L, --load=\"MODULE ARGUMENTS\" Load the specified plugin module with\n" - " the specified argument\n" - " -F, --file=FILENAME Run the specified script\n" - " -C Open a command line on the running TTY\n" - " after startup\n\n" - - " -n Don't load default script file\n", e); -} - -int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d) { - pa_strbuf *buf = NULL; - int c; - assert(conf && argc && argv); - - buf = pa_strbuf_new(); - - if (conf->script_commands) - pa_strbuf_puts(buf, conf->script_commands); - - while ((c = getopt_long(argc, argv, "L:F:ChDnp:kv", long_options, NULL)) != -1) { - switch (c) { - case ARG_HELP: - case 'h': - conf->cmd = PA_CMD_HELP; - break; - - case ARG_VERSION: - conf->cmd = PA_CMD_VERSION; - break; - - case ARG_DUMP_CONF: - conf->cmd = PA_CMD_DUMP_CONF; - break; - - case ARG_DUMP_MODULES: - conf->cmd = PA_CMD_DUMP_MODULES; - break; - - case 'k': - case ARG_KILL: - conf->cmd = PA_CMD_KILL; - break; - - case ARG_CHECK: - conf->cmd = PA_CMD_CHECK; - break; - - case ARG_LOAD: - case 'L': - pa_strbuf_printf(buf, "load-module %s\n", optarg); - break; - - case ARG_FILE: - case 'F': - pa_strbuf_printf(buf, ".include %s\n", optarg); - break; - - case 'C': - pa_strbuf_puts(buf, "load-module module-cli\n"); - break; - - case ARG_DAEMONIZE: - case 'D': - if ((conf->daemonize = optarg ? pa_parse_boolean(optarg) : 1) < 0) { - pa_log(__FILE__": --daemonize expects boolean argument\n"); - goto fail; - } - break; - - case ARG_FAIL: - if ((conf->fail = optarg ? pa_parse_boolean(optarg) : 1) < 0) { - pa_log(__FILE__": --fail expects boolean argument\n"); - goto fail; - } - break; - - case 'v': - case ARG_LOG_LEVEL: - - if (optarg) { - if (pa_daemon_conf_set_log_level(conf, optarg) < 0) { - pa_log(__FILE__": --log-level expects log level argument (either numeric in range 0..4 or one of debug, info, notice, warn, error).\n"); - goto fail; - } - } else { - if (conf->log_level < PA_LOG_LEVEL_MAX-1) - conf->log_level++; - } - - break; - - case ARG_HIGH_PRIORITY: - if ((conf->high_priority = optarg ? pa_parse_boolean(optarg) : 1) < 0) { - pa_log(__FILE__": --high-priority expects boolean argument\n"); - goto fail; - } - break; - - case ARG_DISALLOW_MODULE_LOADING: - if ((conf->disallow_module_loading = optarg ? pa_parse_boolean(optarg) : 1) < 0) { - pa_log(__FILE__": --disallow-module-loading expects boolean argument\n"); - goto fail; - } - break; - - case ARG_USE_PID_FILE: - if ((conf->use_pid_file = optarg ? pa_parse_boolean(optarg) : 1) < 0) { - pa_log(__FILE__": --use-pid-file expects boolean argument\n"); - goto fail; - } - break; - - case 'p': - case ARG_DL_SEARCH_PATH: - pa_xfree(conf->dl_search_path); - conf->dl_search_path = *optarg ? pa_xstrdup(optarg) : NULL; - break; - - case 'n': - pa_xfree(conf->default_script_file); - conf->default_script_file = NULL; - break; - - case ARG_LOG_TARGET: - if (pa_daemon_conf_set_log_target(conf, optarg) < 0) { - pa_log(__FILE__": Invalid log target: use either 'syslog', 'stderr' or 'auto'.\n"); - goto fail; - } - break; - - case ARG_EXIT_IDLE_TIME: - conf->exit_idle_time = atoi(optarg); - break; - - case ARG_MODULE_IDLE_TIME: - conf->module_idle_time = atoi(optarg); - break; - - case ARG_SCACHE_IDLE_TIME: - conf->scache_idle_time = atoi(optarg); - break; - - case ARG_RESAMPLE_METHOD: - if (pa_daemon_conf_set_resample_method(conf, optarg) < 0) { - pa_log(__FILE__": Invalid resample method '%s'.\n", optarg); - goto fail; - } - break; - - default: - goto fail; - } - } - - pa_xfree(conf->script_commands); - conf->script_commands = pa_strbuf_tostring_free(buf); - - if (!conf->script_commands) { - pa_xfree(conf->script_commands); - conf->script_commands = NULL; - } - - *d = optind; - - return 0; - -fail: - if (buf) - pa_strbuf_free(buf); - - return -1; -} diff --git a/polyp/cmdline.h b/polyp/cmdline.h deleted file mode 100644 index e2eaf0d2..00000000 --- a/polyp/cmdline.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef foocmdlinehfoo -#define foocmdlinehfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "daemon-conf.h" - -/* Parese the command line and store its data in *c. Return the index - * of the first unparsed argument in *d. */ -int pa_cmdline_parse(pa_daemon_conf*c, int argc, char *const argv [], int *d); - -/* Show the command line help. The command name is extracted from - * argv[0] which should be passed in argv0. */ -void pa_cmdline_help(const char *argv0); - -#endif diff --git a/polyp/conf-parser.c b/polyp/conf-parser.c deleted file mode 100644 index 507f2bf1..00000000 --- a/polyp/conf-parser.c +++ /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 -#endif - -#include -#include -#include -#include - -#include "conf-parser.h" -#include "log.h" -#include "util.h" -#include "xmalloc.h" - -#define WHITESPACE " \t\n" -#define COMMENTS "#;\n" - -/* Run the user supplied parser for an assignment */ -static int next_assignment(const char *filename, unsigned line, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) { - assert(filename && t && lvalue && rvalue); - - for (; t->parse; t++) - if (!strcmp(lvalue, t->lvalue)) - return t->parse(filename, line, lvalue, rvalue, t->data, userdata); - - pa_log(__FILE__": [%s:%u] Unknown lvalue '%s'.\n", filename, line, lvalue); - - return -1; -} - -/* Returns non-zero when c is contained in s */ -static int in_string(char c, const char *s) { - assert(s); - - for (; *s; s++) - if (*s == c) - return 1; - - return 0; -} - -/* Remove all whitepsapce from the beginning and the end of *s. *s may - * be modified. */ -static char *strip(char *s) { - char *b = s+strspn(s, WHITESPACE); - char *e, *l = NULL; - - for (e = b; *e; e++) - if (!in_string(*e, WHITESPACE)) - l = e; - - if (l) - *(l+1) = 0; - - return b; -} - -/* Parse a variable assignment line */ -static int parse_line(const char *filename, unsigned line, const pa_config_item *t, char *l, void *userdata) { - char *e, *c, *b = l+strspn(l, WHITESPACE); - - if ((c = strpbrk(b, COMMENTS))) - *c = 0; - - if (!*b) - return 0; - - if (!(e = strchr(b, '='))) { - pa_log(__FILE__": [%s:%u] Missing '='.\n", filename, line); - return -1; - } - - *e = 0; - e++; - - return next_assignment(filename, line, t, strip(b), strip(e), userdata); -} - -/* Go through the file and parse each line */ -int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata) { - int r = -1; - unsigned line = 0; - int do_close = !f; - assert(filename && t); - - if (!f && !(f = fopen(filename, "r"))) { - if (errno == ENOENT) { - r = 0; - goto finish; - } - - pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno)); - goto finish; - } - - while (!feof(f)) { - char l[256]; - if (!fgets(l, sizeof(l), f)) { - if (feof(f)) - break; - - pa_log(__FILE__": WARNING: failed to read configuration file '%s': %s\n", filename, strerror(errno)); - goto finish; - } - - if (parse_line(filename, ++line, t, l, userdata) < 0) - goto finish; - } - - r = 0; - -finish: - - if (do_close && f) - fclose(f); - - return r; -} - -int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { - int *i = data; - int32_t k; - assert(filename && lvalue && rvalue && data); - - if (pa_atoi(rvalue, &k) < 0) { - pa_log(__FILE__": [%s:%u] Failed to parse numeric value: %s\n", filename, line, rvalue); - return -1; - } - - *i = (int) k; - return 0; -} - -int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { - int *b = data, k; - assert(filename && lvalue && rvalue && data); - - if ((k = pa_parse_boolean(rvalue)) < 0) { - pa_log(__FILE__": [%s:%u] Failed to parse boolean value: %s\n", filename, line, rvalue); - return -1; - } - - *b = k; - - return 0; -} - -int pa_config_parse_string(const char *filename, PA_GCC_UNUSED unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { - char **s = data; - assert(filename && lvalue && rvalue && data); - - pa_xfree(*s); - *s = *rvalue ? pa_xstrdup(rvalue) : NULL; - return 0; -} diff --git a/polyp/conf-parser.h b/polyp/conf-parser.h deleted file mode 100644 index 2dca3bce..00000000 --- a/polyp/conf-parser.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef fooconfparserhfoo -#define fooconfparserhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -/* An abstract parser for simple, line based, shallow configuration - * files consisting of variable assignments only. */ - -/* Wraps info for parsing a specific configuration variable */ -typedef struct pa_config_item { - const char *lvalue; /* name of the variable */ - int (*parse)(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); /* Function that is called to parse the variable's value */ - void *data; /* Where to store the variable's data */ -} pa_config_item; - -/* The configuration file parsing routine. Expects a table of - * pa_config_items in *t that is terminated by an item where lvalue is - * NULL */ -int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata); - -/* Generic parsers for integers, booleans and strings */ -int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); -int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); -int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); - -#endif diff --git a/polyp/core.c b/polyp/core.c deleted file mode 100644 index 678e8212..00000000 --- a/polyp/core.c +++ /dev/null @@ -1,157 +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 -#endif - -#include -#include -#include -#include - -#include "core.h" -#include "module.h" -#include "sink.h" -#include "source.h" -#include "namereg.h" -#include "util.h" -#include "scache.h" -#include "autoload.h" -#include "xmalloc.h" -#include "subscribe.h" -#include "props.h" -#include "random.h" - -pa_core* pa_core_new(pa_mainloop_api *m) { - pa_core* c; - c = pa_xmalloc(sizeof(pa_core)); - - c->mainloop = m; - c->clients = pa_idxset_new(NULL, NULL); - c->sinks = pa_idxset_new(NULL, NULL); - c->sources = pa_idxset_new(NULL, NULL); - c->source_outputs = pa_idxset_new(NULL, NULL); - c->sink_inputs = pa_idxset_new(NULL, NULL); - - c->default_source_name = c->default_sink_name = NULL; - - c->modules = NULL; - c->namereg = NULL; - c->scache = NULL; - c->autoload_idxset = NULL; - c->autoload_hashmap = NULL; - c->running_as_daemon = 0; - - c->default_sample_spec.format = PA_SAMPLE_S16NE; - c->default_sample_spec.rate = 44100; - c->default_sample_spec.channels = 2; - - c->module_auto_unload_event = NULL; - c->module_defer_unload_event = NULL; - c->scache_auto_unload_event = NULL; - - c->subscription_defer_event = NULL; - c->subscription_event_queue = NULL; - c->subscriptions = NULL; - - c->memblock_stat = pa_memblock_stat_new(); - - c->disallow_module_loading = 0; - - c->quit_event = NULL; - - c->exit_idle_time = -1; - c->module_idle_time = 20; - c->scache_idle_time = 20; - - c->resample_method = PA_RESAMPLER_SRC_SINC_FASTEST; - - pa_property_init(c); - - pa_random(&c->cookie, sizeof(c->cookie)); - -#ifdef SIGPIPE - pa_check_signal_is_blocked(SIGPIPE); -#endif - return c; -} - -void pa_core_free(pa_core *c) { - assert(c); - - pa_module_unload_all(c); - assert(!c->modules); - - assert(pa_idxset_isempty(c->clients)); - pa_idxset_free(c->clients, NULL, NULL); - - assert(pa_idxset_isempty(c->sinks)); - pa_idxset_free(c->sinks, NULL, NULL); - - assert(pa_idxset_isempty(c->sources)); - pa_idxset_free(c->sources, NULL, NULL); - - assert(pa_idxset_isempty(c->source_outputs)); - pa_idxset_free(c->source_outputs, NULL, NULL); - - assert(pa_idxset_isempty(c->sink_inputs)); - pa_idxset_free(c->sink_inputs, NULL, NULL); - - pa_scache_free(c); - pa_namereg_free(c); - pa_autoload_free(c); - pa_subscription_free_all(c); - - if (c->quit_event) - c->mainloop->time_free(c->quit_event); - - pa_xfree(c->default_source_name); - pa_xfree(c->default_sink_name); - - pa_memblock_stat_unref(c->memblock_stat); - - pa_property_cleanup(c); - - pa_xfree(c); -} - -static void quit_callback(pa_mainloop_api*m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { - pa_core *c = userdata; - assert(c->quit_event = e); - - m->quit(m, 0); -} - -void pa_core_check_quit(pa_core *c) { - assert(c); - - if (!c->quit_event && c->exit_idle_time >= 0 && pa_idxset_size(c->clients) == 0) { - struct timeval tv; - pa_gettimeofday(&tv); - tv.tv_sec+= c->exit_idle_time; - c->quit_event = c->mainloop->time_new(c->mainloop, &tv, quit_callback, c); - } else if (c->quit_event && pa_idxset_size(c->clients) > 0) { - c->mainloop->time_free(c->quit_event); - c->quit_event = NULL; - } -} - diff --git a/polyp/core.h b/polyp/core.h deleted file mode 100644 index 9241fcd8..00000000 --- a/polyp/core.h +++ /dev/null @@ -1,82 +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. -***/ - -typedef struct pa_core pa_core; - -#include "idxset.h" -#include "hashmap.h" -#include "mainloop-api.h" -#include "sample.h" -#include "memblock.h" -#include "resampler.h" -#include "queue.h" -#include "subscribe.h" - -/* The core structure of polypaudio. Every polypaudio daemon contains - * exactly one of these. It is used for storing kind of global - * variables for the daemon. */ - -struct pa_core { - /* A random value which may be used to identify this instance of - * polypaudio. Not cryptographically secure in any way. */ - uint32_t cookie; - - pa_mainloop_api *mainloop; - - /* idxset of all kinds of entities */ - pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *autoload_idxset; - - /* Some hashmaps for all sorts of entities */ - pa_hashmap *namereg, *autoload_hashmap, *properties; - - /* The name of the default sink/source */ - char *default_source_name, *default_sink_name; - - pa_sample_spec default_sample_spec; - pa_time_event *module_auto_unload_event; - pa_defer_event *module_defer_unload_event; - - pa_defer_event *subscription_defer_event; - pa_queue *subscription_event_queue; - pa_subscription *subscriptions; - - pa_memblock_stat *memblock_stat; - - int disallow_module_loading, running_as_daemon; - int exit_idle_time, module_idle_time, scache_idle_time; - - pa_time_event *quit_event; - - pa_time_event *scache_auto_unload_event; - - pa_resample_method_t resample_method; -}; - -pa_core* pa_core_new(pa_mainloop_api *m); -void pa_core_free(pa_core*c); - -/* Check whether noone is connected to this core */ -void pa_core_check_quit(pa_core *c); - -#endif diff --git a/polyp/cpulimit-test.c b/polyp/cpulimit-test.c deleted file mode 100644 index 598b2dd2..00000000 --- a/polyp/cpulimit-test.c +++ /dev/null @@ -1,92 +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 -#endif - -#include -#include -#include -#include -#include - -#include "cpulimit.h" -#include "mainloop.h" -#include "gccmacro.h" - -#ifdef TEST2 -#include "mainloop-signal.h" -#endif - -/* A simple example for testing the cpulimit subsystem */ - -static time_t start; - -#ifdef TEST2 - -static void func(pa_mainloop_api *m, PA_GCC_UNUSED pa_signal_event *e, PA_GCC_UNUSED int sig, PA_GCC_UNUSED void *userdata) { - time_t now; - time(&now); - - if ((now - start) >= 30) { - m->quit(m, 1); - fprintf(stderr, "Test failed\n"); - } else - raise(SIGUSR1); -} - -#endif - -int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { - pa_mainloop *m; - - m = pa_mainloop_new(); - assert(m); - - pa_cpu_limit_init(pa_mainloop_get_api(m)); - - time(&start); - -#ifdef TEST2 - pa_signal_init(pa_mainloop_get_api(m)); - pa_signal_new(SIGUSR1, func, NULL); - raise(SIGUSR1); - pa_mainloop_run(m, NULL); - pa_signal_done(); -#else - for (;;) { - time_t now; - time(&now); - - if ((now - start) >= 30) { - fprintf(stderr, "Test failed\n"); - break; - } - } -#endif - - pa_cpu_limit_done(); - - pa_mainloop_free(m); - - return 0; -} diff --git a/polyp/cpulimit.c b/polyp/cpulimit.c deleted file mode 100644 index a834b094..00000000 --- a/polyp/cpulimit.c +++ /dev/null @@ -1,236 +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 -#endif - -#include "cpulimit.h" -#include "util.h" -#include "log.h" - -#ifdef HAVE_SIGXCPU - -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SYS_RESOURCE_H -#include -#endif - -/* This module implements a watchdog that makes sure that the current - * process doesn't consume more than 70% CPU time for 10 seconds. This - * is very useful when using SCHED_FIFO scheduling which effectively - * disables multitasking. */ - -/* Method of operation: Using SIGXCPU a signal handler is called every - * 10s process CPU time. That function checks if less than 14s system - * time have passed. In that case, it tries to contact the main event - * loop through a pipe. After two additional seconds it is checked - * whether the main event loop contact was successful. If not, the - * program is terminated forcibly. */ - -/* Utilize this much CPU time at maximum */ -#define CPUTIME_PERCENT 70 - -/* Check every 10s */ -#define CPUTIME_INTERVAL_SOFT (10) - -/* Recheck after 2s */ -#define CPUTIME_INTERVAL_HARD (2) - -/* Time of the last CPU load check */ -static time_t last_time = 0; - -/* Pipe for communicating with the main loop */ -static int the_pipe[2] = {-1, -1}; - -/* Main event loop and IO event for the FIFO */ -static pa_mainloop_api *api = NULL; -static pa_io_event *io_event = NULL; - -/* Saved sigaction struct for SIGXCPU */ -static struct sigaction sigaction_prev; - -/* Nonzero after pa_cpu_limit_init() */ -static int installed = 0; - -/* The current state of operation */ -static enum { - PHASE_IDLE, /* Normal state */ - PHASE_SOFT /* After CPU overload has been detected */ -} phase = PHASE_IDLE; - -/* Reset the SIGXCPU timer to the next t seconds */ -static void reset_cpu_time(int t) { - int r; - long n; - struct rlimit rl; - struct rusage ru; - - /* Get the current CPU time of the current process */ - r = getrusage(RUSAGE_SELF, &ru); - assert(r >= 0); - - n = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec + t; - - r = getrlimit(RLIMIT_CPU, &rl); - assert(r >= 0); - - rl.rlim_cur = n; - r = setrlimit(RLIMIT_CPU, &rl); - assert(r >= 0); -} - -/* A simple, thread-safe puts() work-alike */ -static void write_err(const char *p) { - pa_loop_write(2, p, strlen(p)); -} - -/* The signal handler, called on every SIGXCPU */ -static void signal_handler(int sig) { - assert(sig == SIGXCPU); - - if (phase == PHASE_IDLE) { - time_t now; - -#ifdef PRINT_CPU_LOAD - char t[256]; -#endif - - time(&now); - -#ifdef PRINT_CPU_LOAD - snprintf(t, sizeof(t), "Using %0.1f%% CPU\n", (double)CPUTIME_INTERVAL_SOFT/(now-last_time)*100); - write_err(t); -#endif - - if (CPUTIME_INTERVAL_SOFT >= ((now-last_time)*(double)CPUTIME_PERCENT/100)) { - static const char c = 'X'; - - write_err("Soft CPU time limit exhausted, terminating.\n"); - - /* Try a soft cleanup */ - write(the_pipe[1], &c, sizeof(c)); - phase = PHASE_SOFT; - reset_cpu_time(CPUTIME_INTERVAL_HARD); - - } else { - - /* Everything's fine */ - reset_cpu_time(CPUTIME_INTERVAL_SOFT); - last_time = now; - } - - } else if (phase == PHASE_SOFT) { - write_err("Hard CPU time limit exhausted, terminating forcibly.\n"); - _exit(1); /* Forced exit */ - } -} - -/* Callback for IO events on the FIFO */ -static void callback(pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags f, void *userdata) { - char c; - assert(m && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == the_pipe[0]); - read(the_pipe[0], &c, sizeof(c)); - m->quit(m, 1); /* Quit the main loop */ -} - -/* Initializes CPU load limiter */ -int pa_cpu_limit_init(pa_mainloop_api *m) { - struct sigaction sa; - assert(m && !api && !io_event && the_pipe[0] == -1 && the_pipe[1] == -1 && !installed); - - time(&last_time); - - /* Prepare the main loop pipe */ - if (pipe(the_pipe) < 0) { - pa_log(__FILE__": pipe() failed: %s\n", strerror(errno)); - return -1; - } - - pa_make_nonblock_fd(the_pipe[0]); - pa_make_nonblock_fd(the_pipe[1]); - pa_fd_set_cloexec(the_pipe[0], 1); - pa_fd_set_cloexec(the_pipe[1], 1); - - api = m; - io_event = api->io_new(m, the_pipe[0], PA_IO_EVENT_INPUT, callback, NULL); - - phase = PHASE_IDLE; - - /* Install signal handler for SIGXCPU */ - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = signal_handler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - - if (sigaction(SIGXCPU, &sa, &sigaction_prev) < 0) { - pa_cpu_limit_done(); - return -1; - } - - installed = 1; - - reset_cpu_time(CPUTIME_INTERVAL_SOFT); - - return 0; -} - -/* Shutdown CPU load limiter */ -void pa_cpu_limit_done(void) { - int r; - - if (io_event) { - assert(api); - api->io_free(io_event); - io_event = NULL; - api = NULL; - } - - if (the_pipe[0] >= 0) - close(the_pipe[0]); - if (the_pipe[1] >= 0) - close(the_pipe[1]); - the_pipe[0] = the_pipe[1] = -1; - - if (installed) { - r = sigaction(SIGXCPU, &sigaction_prev, NULL); - assert(r >= 0); - installed = 0; - } -} - -#else /* HAVE_SIGXCPU */ - -int pa_cpu_limit_init(PA_GCC_UNUSED pa_mainloop_api *m) { - return 0; -} - -void pa_cpu_limit_done(void) { -} - -#endif diff --git a/polyp/cpulimit.h b/polyp/cpulimit.h deleted file mode 100644 index 8ca4f8d9..00000000 --- a/polyp/cpulimit.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef foocpulimithfoo -#define foocpulimithfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "mainloop-api.h" - -/* This kills the polypaudio process if it eats more than 70% of the - * CPU time. This is build around setrlimit() and SIGXCPU. It is handy - * in case of using SCHED_FIFO which may freeze the whole machine */ - -int pa_cpu_limit_init(pa_mainloop_api *m); -void pa_cpu_limit_done(void); - -#endif diff --git a/polyp/daemon-conf.c b/polyp/daemon-conf.c deleted file mode 100644 index 103cf46a..00000000 --- a/polyp/daemon-conf.c +++ /dev/null @@ -1,297 +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 -#endif - -#include -#include -#include -#include -#include - -#include "daemon-conf.h" -#include "util.h" -#include "xmalloc.h" -#include "strbuf.h" -#include "conf-parser.h" -#include "resampler.h" - -#ifndef DEFAULT_CONFIG_DIR -# ifndef OS_IS_WIN32 -# define DEFAULT_CONFIG_DIR "/etc/polypaudio" -# else -# define DEFAULT_CONFIG_DIR "%POLYP_ROOT%" -# endif -#endif - -#ifndef OS_IS_WIN32 -# define PATH_SEP "/" -#else -# define PATH_SEP "\\" -#endif - -#define DEFAULT_SCRIPT_FILE DEFAULT_CONFIG_DIR PATH_SEP "default.pa" -#define DEFAULT_SCRIPT_FILE_USER ".polypaudio" PATH_SEP "default.pa" -#define DEFAULT_CONFIG_FILE DEFAULT_CONFIG_DIR PATH_SEP "daemon.conf" -#define DEFAULT_CONFIG_FILE_USER ".polypaudio" PATH_SEP "daemon.conf" - -#define ENV_SCRIPT_FILE "POLYP_SCRIPT" -#define ENV_CONFIG_FILE "POLYP_CONFIG" -#define ENV_DL_SEARCH_PATH "POLYP_DLPATH" - -static const pa_daemon_conf default_conf = { - .cmd = PA_CMD_DAEMON, - .daemonize = 0, - .fail = 1, - .high_priority = 0, - .disallow_module_loading = 0, - .exit_idle_time = -1, - .module_idle_time = 20, - .scache_idle_time = 20, - .auto_log_target = 1, - .script_commands = NULL, - .dl_search_path = NULL, - .default_script_file = NULL, - .log_target = PA_LOG_SYSLOG, - .log_level = PA_LOG_NOTICE, - .resample_method = PA_RESAMPLER_SRC_SINC_FASTEST, - .config_file = NULL, - .use_pid_file = 1 -}; - -pa_daemon_conf* pa_daemon_conf_new(void) { - FILE *f; - pa_daemon_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf)); - - if ((f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file))) - fclose(f); - -#ifdef DLSEARCHPATH - c->dl_search_path = pa_xstrdup(DLSEARCHPATH); -#endif - return c; -} - -void pa_daemon_conf_free(pa_daemon_conf *c) { - assert(c); - pa_xfree(c->script_commands); - pa_xfree(c->dl_search_path); - pa_xfree(c->default_script_file); - pa_xfree(c->config_file); - pa_xfree(c); -} - -int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string) { - assert(c && string); - - if (!strcmp(string, "auto")) - c->auto_log_target = 1; - else if (!strcmp(string, "syslog")) { - c->auto_log_target = 0; - c->log_target = PA_LOG_SYSLOG; - } else if (!strcmp(string, "stderr")) { - c->auto_log_target = 0; - c->log_target = PA_LOG_STDERR; - } else - return -1; - - return 0; -} - -int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string) { - uint32_t u; - assert(c && string); - - if (pa_atou(string, &u) >= 0) { - if (u >= PA_LOG_LEVEL_MAX) - return -1; - - c->log_level = (pa_log_level_t) u; - } else if (pa_startswith(string, "debug")) - c->log_level = PA_LOG_DEBUG; - else if (pa_startswith(string, "info")) - c->log_level = PA_LOG_INFO; - else if (pa_startswith(string, "notice")) - c->log_level = PA_LOG_NOTICE; - else if (pa_startswith(string, "warn")) - c->log_level = PA_LOG_WARN; - else if (pa_startswith(string, "err")) - c->log_level = PA_LOG_ERROR; - else - return -1; - - return 0; -} - -int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) { - int m; - assert(c && string); - - if ((m = pa_parse_resample_method(string)) < 0) - return -1; - - c->resample_method = m; - return 0; -} - -static int parse_log_target(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { - pa_daemon_conf *c = data; - assert(filename && lvalue && rvalue && data); - - if (pa_daemon_conf_set_log_target(c, rvalue) < 0) { - pa_log(__FILE__": [%s:%u] Invalid log target '%s'.\n", filename, line, rvalue); - return -1; - } - - return 0; -} - -static int parse_log_level(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { - pa_daemon_conf *c = data; - assert(filename && lvalue && rvalue && data); - - if (pa_daemon_conf_set_log_level(c, rvalue) < 0) { - pa_log(__FILE__": [%s:%u] Invalid log level '%s'.\n", filename, line, rvalue); - return -1; - } - - return 0; -} - -static int parse_resample_method(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { - pa_daemon_conf *c = data; - assert(filename && lvalue && rvalue && data); - - if (pa_daemon_conf_set_resample_method(c, rvalue) < 0) { - pa_log(__FILE__": [%s:%u] Inavalid resample method '%s'.\n", filename, line, rvalue); - return -1; - } - - return 0; -} - -int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { - int r = -1; - FILE *f = NULL; - - pa_config_item table[] = { - { "daemonize", pa_config_parse_bool, NULL }, - { "fail", pa_config_parse_bool, NULL }, - { "high-priority", pa_config_parse_bool, NULL }, - { "disallow-module-loading", pa_config_parse_bool, NULL }, - { "exit-idle-time", pa_config_parse_int, NULL }, - { "module-idle-time", pa_config_parse_int, NULL }, - { "scache-idle-time", pa_config_parse_int, NULL }, - { "dl-search-path", pa_config_parse_string, NULL }, - { "default-script-file", pa_config_parse_string, NULL }, - { "log-target", parse_log_target, NULL }, - { "log-level", parse_log_level, NULL }, - { "verbose", parse_log_level, NULL }, - { "resample-method", parse_resample_method, NULL }, - { "use-pid-file", pa_config_parse_bool, NULL }, - { NULL, NULL, NULL }, - }; - - table[0].data = &c->daemonize; - table[1].data = &c->fail; - table[2].data = &c->high_priority; - table[3].data = &c->disallow_module_loading; - table[4].data = &c->exit_idle_time; - table[5].data = &c->module_idle_time; - table[6].data = &c->scache_idle_time; - table[7].data = &c->dl_search_path; - table[8].data = &c->default_script_file; - table[9].data = c; - table[10].data = c; - table[11].data = c; - table[12].data = c; - table[13].data = &c->use_pid_file; - - pa_xfree(c->config_file); - c->config_file = NULL; - - f = filename ? - fopen(c->config_file = pa_xstrdup(filename), "r") : - pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file); - - if (!f && errno != ENOENT) { - pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno)); - goto finish; - } - - r = f ? pa_config_parse(c->config_file, f, table, NULL) : 0; - -finish: - if (f) - fclose(f); - - return r; -} - -int pa_daemon_conf_env(pa_daemon_conf *c) { - char *e; - - if ((e = getenv(ENV_DL_SEARCH_PATH))) { - pa_xfree(c->dl_search_path); - c->dl_search_path = pa_xstrdup(e); - } - if ((e = getenv(ENV_SCRIPT_FILE))) { - pa_xfree(c->default_script_file); - c->default_script_file = pa_xstrdup(e); - } - - return 0; -} - -static const char* const log_level_to_string[] = { - [PA_LOG_DEBUG] = "debug", - [PA_LOG_INFO] = "info", - [PA_LOG_NOTICE] = "notice", - [PA_LOG_WARN] = "warning", - [PA_LOG_ERROR] = "error" -}; - -char *pa_daemon_conf_dump(pa_daemon_conf *c) { - pa_strbuf *s = pa_strbuf_new(); - - if (c->config_file) - pa_strbuf_printf(s, "### Read from configuration file: %s ###\n", c->config_file); - - assert(c->log_level <= PA_LOG_LEVEL_MAX); - - pa_strbuf_printf(s, "daemonize = %i\n", !!c->daemonize); - pa_strbuf_printf(s, "fail = %i\n", !!c->fail); - pa_strbuf_printf(s, "high-priority = %i\n", !!c->high_priority); - pa_strbuf_printf(s, "disallow-module-loading = %i\n", !!c->disallow_module_loading); - pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time); - pa_strbuf_printf(s, "module-idle-time = %i\n", c->module_idle_time); - pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time); - pa_strbuf_printf(s, "dl-search-path = %s\n", c->dl_search_path ? c->dl_search_path : ""); - pa_strbuf_printf(s, "default-script-file = %s\n", c->default_script_file); - pa_strbuf_printf(s, "log-target = %s\n", c->auto_log_target ? "auto" : (c->log_target == PA_LOG_SYSLOG ? "syslog" : "stderr")); - pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]); - pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method)); - pa_strbuf_printf(s, "use-pid-file = %i\n", c->use_pid_file); - - return pa_strbuf_tostring_free(s); -} diff --git a/polyp/daemon-conf.h b/polyp/daemon-conf.h deleted file mode 100644 index cebc0bd6..00000000 --- a/polyp/daemon-conf.h +++ /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 */ -typedef enum pa_daemon_conf_cmd { - PA_CMD_DAEMON, /* the default */ - PA_CMD_HELP, - PA_CMD_VERSION, - PA_CMD_DUMP_CONF, - PA_CMD_DUMP_MODULES, - PA_CMD_KILL, - PA_CMD_CHECK -} pa_daemon_conf_cmd_t; - -/* A structure containing configuration data for the Polypaudio server . */ -typedef struct pa_daemon_conf { - pa_daemon_conf_cmd_t cmd; - int daemonize, - fail, - high_priority, - disallow_module_loading, - exit_idle_time, - module_idle_time, - scache_idle_time, - auto_log_target, - use_pid_file; - char *script_commands, *dl_search_path, *default_script_file; - pa_log_target_t log_target; - pa_log_level_t log_level; - int resample_method; - char *config_file; -} pa_daemon_conf; - -/* Allocate a new structure and fill it with sane defaults */ -pa_daemon_conf* pa_daemon_conf_new(void); -void pa_daemon_conf_free(pa_daemon_conf*c); - -/* Load configuration data from the specified file overwriting the - * current settings in *c. If filename is NULL load the default daemon - * configuration file */ -int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename); - -/* Pretty print the current configuration data of the daemon. The - * returned string has to be freed manually. The output of this - * function may be parsed with pa_daemon_conf_load(). */ -char *pa_daemon_conf_dump(pa_daemon_conf *c); - -/* Load the configuration data from the process' environment - * overwriting the current settings in *c. */ -int pa_daemon_conf_env(pa_daemon_conf *c); - -/* Set these configuration variables in the structure by passing a string */ -int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string); -int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string); -int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string); - -#endif diff --git a/polyp/daemon.conf.in b/polyp/daemon.conf.in deleted file mode 100644 index d5373018..00000000 --- a/polyp/daemon.conf.in +++ /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 index 3aaeeaf0..00000000 --- a/polyp/default.pa.in +++ /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/default.pa.win32 b/polyp/default.pa.win32 deleted file mode 100644 index 3478adab..00000000 --- a/polyp/default.pa.win32 +++ /dev/null @@ -1,43 +0,0 @@ -# -# This file is part of polypaudio. -# -# polypaudio is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# polypaudio is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with polypaudio; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - - -# Load audio drivers statically - -load-module module-waveout sink_name=output source_name=input -load-module module-null-sink - -# Load audio drivers automatically on access - -#add-autoload-sink output module-waveout sink_name=output source_name=input -#add-autoload-source input module-waveout sink_name=output source_name=input - -# Load several protocols -#load-module module-esound-protocol-tcp -#load-module module-native-protocol-tcp -#load-module module-simple-protocol-tcp -#load-module module-cli-protocol-tcp - -# Make some devices default -set-default-sink output -set-default-source input - -.nofail - -# Load something to the sample cache -load-sample x11-bell %WINDIR%\Media\ding.wav -load-sample-dir-lazy %WINDIR%\Media\*.wav diff --git a/polyp/depmod.py b/polyp/depmod.py deleted file mode 100755 index 7bb223b1..00000000 --- a/polyp/depmod.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/python -# $Id$ -# -# This file is part of polypaudio. -# -# polypaudio is free software; you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# polypaudio is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with polypaudio; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA. - -import sys, os, string - -exported_symbols = {} -imported_symbols = {} - -for fn in sys.argv[1:]: - f = os.popen("nm '%s'" % fn, "r") - - imported_symbols[fn] = [] - - for line in f: - sym_address = line[:7].strip() - sym_type = line[9].strip() - sym_name = line[11:].strip() - - if sym_name in ('_fini', '_init'): - continue - - if sym_type in ('T', 'B', 'R', 'D' 'G', 'S', 'D'): - if exported_symbols.has_key(sym_name): - sys.stderr.write("CONFLICT: %s defined in both '%s' and '%s'.\n" % (sym_name, fn, exported_symbols[sym_name])) - else: - exported_symbols[sym_name] = fn - elif sym_type in ('U',): - if sym_name[:3] == 'pa_': - imported_symbols[fn].append(sym_name) - - f.close() - -dependencies = {} -unresolved_symbols = {} - -for fn in imported_symbols: - dependencies[fn] = [] - - for sym in imported_symbols[fn]: - if exported_symbols.has_key(sym): - if exported_symbols[sym] not in dependencies[fn]: - dependencies[fn].append(exported_symbols[sym]) - else: - if unresolved_symbols.has_key(sym): - unresolved_symbols[sym].append(fn) - else: - unresolved_symbols[sym] = [fn] - -for sym, files in unresolved_symbols.iteritems(): - print "WARNING: Unresolved symbol '%s' in %s" % (sym, `files`) - -k = dependencies.keys() -k.sort() -for fn in k: - dependencies[fn].sort() - print "%s: %s" % (fn, string.join(dependencies[fn], " ")) diff --git a/polyp/dllmain.c b/polyp/dllmain.c deleted file mode 100644 index d1d120ab..00000000 --- a/polyp/dllmain.c +++ /dev/null @@ -1,46 +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 -#endif - -#ifdef OS_IS_WIN32 - -#include -#include -#include - -#include - -extern pa_set_root(HANDLE handle); - -BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { - if (fdwReason != DLL_PROCESS_ATTACH) - return TRUE; - - if (!pa_set_root(hinstDLL)) - return FALSE; - - return TRUE; -} - -#endif /* OS_IS_WIN32 */ diff --git a/polyp/dumpmodules.c b/polyp/dumpmodules.c deleted file mode 100644 index 6864b965..00000000 --- a/polyp/dumpmodules.c +++ /dev/null @@ -1,99 +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 -#endif - -#include -#include -#include -#include -#include - -#include "dumpmodules.h" -#include "modinfo.h" -#include "util.h" - -#define PREFIX "module-" - -static void short_info(const char *name, PA_GCC_UNUSED const char *path, pa_modinfo *i) { - assert(name && i); - printf("%-40s%s\n", name, i->description ? i->description : "n/a"); -} - -static void long_info(const char *name, const char *path, pa_modinfo *i) { - static int nl = 0; - assert(name && i); - - if (nl) - printf("\n"); - - nl = 1; - - printf("Name: %s\n", name); - - if (!i->description && !i->version && !i->author && !i->usage) - printf("No module information available\n"); - else { - if (i->version) - printf("Version: %s\n", i->version); - if (i->description) - printf("Description: %s\n", i->description); - if (i->author) - printf("Author: %s\n", i->author); - if (i->usage) - printf("Usage: %s\n", i->usage); - } - - if (path) - printf("Path: %s\n", path); -} - -static void show_info(const char *name, const char *path, void (*info)(const char *name, const char *path, pa_modinfo*i)) { - pa_modinfo *i; - - if ((i = pa_modinfo_get_by_name(path ? path : name))) { - info(name, path, i); - pa_modinfo_free(i); - } -} - -static int callback(const char *path, lt_ptr data) { - const char *e; - pa_daemon_conf *c = (data); - - e = pa_path_get_filename(path); - - if (strlen(e) > sizeof(PREFIX)-1 && !strncmp(e, PREFIX, sizeof(PREFIX)-1)) - show_info(e, path, c->log_level >= PA_LOG_INFO ? long_info : short_info); - - return 0; -} - -void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]) { - if (argc > 0) { - int i; - for (i = 0; i < argc; i++) - show_info(argv[i], NULL, long_info); - } else - lt_dlforeachfile(NULL, callback, c); -} diff --git a/polyp/dumpmodules.h b/polyp/dumpmodules.h deleted file mode 100644 index 968d2de9..00000000 --- a/polyp/dumpmodules.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef foodumpmoduleshfoo -#define foodumpmoduleshfoo - -/* $Id*/ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "daemon-conf.h" - -/* Dump all available modules to STDOUT. If argc > 0 print information - * about the modules specified in argv[] instead. */ -void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]); - -#endif diff --git a/polyp/dynarray.c b/polyp/dynarray.c deleted file mode 100644 index 435fd768..00000000 --- a/polyp/dynarray.c +++ /dev/null @@ -1,102 +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 -#endif - -#include -#include -#include - -#include "dynarray.h" -#include "xmalloc.h" - -/* If the array becomes to small, increase its size by 100 entries */ -#define INCREASE_BY 100 - -struct pa_dynarray { - void **data; - unsigned n_allocated, n_entries; -}; - -pa_dynarray* pa_dynarray_new(void) { - pa_dynarray *a; - a = pa_xnew(pa_dynarray, 1); - a->data = NULL; - a->n_entries = 0; - a->n_allocated = 0; - return a; -} - -void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata) { - unsigned i; - assert(a); - - if (func) - for (i = 0; i < a->n_entries; i++) - if (a->data[i]) - func(a->data[i], userdata); - - pa_xfree(a->data); - pa_xfree(a); -} - -void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p) { - assert(a); - - if (i >= a->n_allocated) { - unsigned n; - - if (!p) - return; - - n = i+INCREASE_BY; - a->data = pa_xrealloc(a->data, sizeof(void*)*n); - memset(a->data+a->n_allocated, 0, sizeof(void*)*(n-a->n_allocated)); - a->n_allocated = n; - } - - a->data[i] = p; - - if (i >= a->n_entries) - a->n_entries = i+1; -} - -unsigned pa_dynarray_append(pa_dynarray*a, void *p) { - unsigned i = a->n_entries; - pa_dynarray_put(a, i, p); - return i; -} - -void *pa_dynarray_get(pa_dynarray*a, unsigned i) { - assert(a); - if (i >= a->n_allocated) - return NULL; - - assert(a->data); - return a->data[i]; -} - -unsigned pa_dynarray_size(pa_dynarray*a) { - assert(a); - return a->n_entries; -} diff --git a/polyp/dynarray.h b/polyp/dynarray.h deleted file mode 100644 index 9b1601ba..00000000 --- a/polyp/dynarray.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef foodynarrayhfoo -#define foodynarrayhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -typedef struct pa_dynarray pa_dynarray; - -/* Implementation of a simple dynamically sized array. The array - * expands if required, but doesn't shrink if possible. Memory - * management of the array's entries is the user's job. */ - -pa_dynarray* pa_dynarray_new(void); - -/* Free the array calling the specified function for every entry in - * the array. The function may be NULL. */ -void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata); - -/* Store p at position i in the array */ -void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p); - -/* Store p a the first free position in the array. Returns the index - * of that entry. If entries are removed from the array their position - * are not filled any more by this function. */ -unsigned pa_dynarray_append(pa_dynarray*a, void *p); - -void *pa_dynarray_get(pa_dynarray*a, unsigned i); - -unsigned pa_dynarray_size(pa_dynarray*a); - -#endif diff --git a/polyp/endianmacros.h b/polyp/endianmacros.h deleted file mode 100644 index 3ab1826a..00000000 --- a/polyp/endianmacros.h +++ /dev/null @@ -1,77 +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 - -#ifdef HAVE_CONFIG_H -#include -#endif - -#define INT16_SWAP(x) ( (int16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) ) -#define UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) ) -#define INT32_SWAP(x) ( (int32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) ) -#define UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) ) - -#define MAYBE_INT32_SWAP(c,x) ((c) ? INT32_SWAP(x) : x) -#define MAYBE_UINT32_SWAP(c,x) ((c) ? UINT32_SWAP(x) : x) - -#ifdef WORDS_BIGENDIAN - #define INT16_FROM_LE(x) INT16_SWAP(x) - #define INT16_FROM_BE(x) ((int16_t)(x)) - - #define INT16_TO_LE(x) INT16_SWAP(x) - #define INT16_TO_BE(x) ((int16_t)(x)) - - #define UINT16_FROM_LE(x) UINT16_SWAP(x) - #define UINT16_FROM_BE(x) ((uint16_t)(x)) - - #define INT32_FROM_LE(x) INT32_SWAP(x) - #define INT32_FROM_BE(x) ((int32_t)(x)) - - #define UINT32_FROM_LE(x) UINT32_SWAP(x) - #define UINT32_FROM_BE(x) ((uint32_t)(x)) - - #define UINT32_TO_LE(x) UINT32_SWAP(x) - #define UINT32_TO_BE(x) ((uint32_t)(x)) -#else - #define INT16_FROM_LE(x) ((int16_t)(x)) - #define INT16_FROM_BE(x) INT16_SWAP(x) - - #define INT16_TO_LE(x) ((int16_t)(x)) - #define INT16_TO_BE(x) INT16_SWAP(x) - - #define UINT16_FROM_LE(x) ((uint16_t)(x)) - #define UINT16_FROM_BE(x) UINT16_SWAP(x) - - #define INT32_FROM_LE(x) ((int32_t)(x)) - #define INT32_FROM_BE(x) INT32_SWAP(x) - - #define UINT32_FROM_LE(x) ((uint32_t)(x)) - #define UINT32_FROM_BE(x) UINT32_SWAP(x) - - #define UINT32_TO_LE(x) ((uint32_t)(x)) - #define UINT32_TO_BE(x) UINT32_SWAP(x) -#endif - -#endif diff --git a/polyp/esdcompat.sh.in b/polyp/esdcompat.sh.in deleted file mode 100755 index 76023f52..00000000 --- a/polyp/esdcompat.sh.in +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/sh - -# $Id$ -# -# This file is part of polypaudio. -# -# polypaudio is free software; you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# polypaudio is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with polypaudio; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA. - -VERSION_STRING="@PACKAGE_NAME@ esd wrapper @PACKAGE_VERSION@" - -fail() { - echo "ERROR: $1" - exit 1 -} - -ARGS=" --log-target=syslog" - -for N in $(seq $#) ; do - - case "$1" in - "") - ;; - - -v|--version) - echo "$VERSION_STRING" - exit 0 - ;; - - -h|--help) - cat <> 3; */ - - /* A-law using even bit inversion */ - if (pcm_val >= 0) { - mask = 0xD5; /* sign (7th) bit = 1 */ - } else { - mask = 0x55; /* sign bit = 0 */ - pcm_val = -pcm_val - 1; - } - - /* Convert the scaled magnitude to segment number. */ - seg = search(pcm_val, seg_aend, 8); - - /* Combine the sign, segment, and quantization bits. */ - - if (seg >= 8) /* out of range, return maximum value. */ - return (unsigned char) (0x7F ^ mask); - else { - aval = (unsigned char) seg << SEG_SHIFT; - if (seg < 2) - aval |= (pcm_val >> 1) & QUANT_MASK; - else - aval |= (pcm_val >> seg) & QUANT_MASK; - return (aval ^ mask); - } -} - -/* - * alaw2linear() - Convert an A-law value to 16-bit signed linear PCM - * - */ -int16_t st_alaw2linear16( - unsigned char a_val) -{ - int16_t t; - int16_t seg; - - a_val ^= 0x55; - - t = (a_val & QUANT_MASK) << 4; - seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; - switch (seg) { - case 0: - t += 8; - break; - case 1: - t += 0x108; - break; - default: - t += 0x108; - t <<= seg - 1; - } - return ((a_val & SIGN_BIT) ? t : -t); -} -#endif /* !FAST_ALAW_CONVERSION */ - -#define BIAS (0x84) /* Bias for linear code. */ -#define CLIP 8159 - -#ifndef FAST_ULAW_CONVERSION -/* - * linear2ulaw() accepts a 14-bit signed integer and encodes it as u-law data - * stored in a unsigned char. This function should only be called with - * the data shifted such that it only contains information in the lower - * 14-bits. - * - * In order to simplify the encoding process, the original linear magnitude - * is biased by adding 33 which shifts the encoding range from (0 - 8158) to - * (33 - 8191). The result can be seen in the following encoding table: - * - * Biased Linear Input Code Compressed Code - * ------------------------ --------------- - * 00000001wxyza 000wxyz - * 0000001wxyzab 001wxyz - * 000001wxyzabc 010wxyz - * 00001wxyzabcd 011wxyz - * 0001wxyzabcde 100wxyz - * 001wxyzabcdef 101wxyz - * 01wxyzabcdefg 110wxyz - * 1wxyzabcdefgh 111wxyz - * - * Each biased linear code has a leading 1 which identifies the segment - * number. The value of the segment number is equal to 7 minus the number - * of leading 0's. The quantization interval is directly available as the - * four bits wxyz. * The trailing bits (a - h) are ignored. - * - * Ordinarily the complement of the resulting code word is used for - * transmission, and so the code word is complemented before it is returned. - * - * For further information see John C. Bellamy's Digital Telephony, 1982, - * John Wiley & Sons, pps 98-111 and 472-476. - */ -unsigned char st_14linear2ulaw( - int16_t pcm_val) /* 2's complement (14-bit range) */ -{ - int16_t mask; - int16_t seg; - unsigned char uval; - - /* Have calling software do it since its already doing a shift - * from 32-bits down to 16-bits. - */ - /* pcm_val = pcm_val >> 2; */ - - /* u-law inverts all bits */ - /* Get the sign and the magnitude of the value. */ - if (pcm_val < 0) { - pcm_val = -pcm_val; - mask = 0x7F; - } else { - mask = 0xFF; - } - if ( pcm_val > CLIP ) pcm_val = CLIP; /* clip the magnitude */ - pcm_val += (BIAS >> 2); - - /* Convert the scaled magnitude to segment number. */ - seg = search(pcm_val, seg_uend, 8); - - /* - * Combine the sign, segment, quantization bits; - * and complement the code word. - */ - if (seg >= 8) /* out of range, return maximum value. */ - return (unsigned char) (0x7F ^ mask); - else { - uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF); - return (uval ^ mask); - } - -} - -/* - * ulaw2linear() - Convert a u-law value to 16-bit linear PCM - * - * First, a biased linear code is derived from the code word. An unbiased - * output can then be obtained by subtracting 33 from the biased code. - * - * Note that this function expects to be passed the complement of the - * original code word. This is in keeping with ISDN conventions. - */ -int16_t st_ulaw2linear16( - unsigned char u_val) -{ - int16_t t; - - /* Complement to obtain normal u-law value. */ - u_val = ~u_val; - - /* - * Extract and bias the quantization bits. Then - * shift up by the segment number and subtract out the bias. - */ - t = ((u_val & QUANT_MASK) << 3) + BIAS; - t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; - - return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); -} -#endif /* !FAST_ULAW_CONVERSION */ - -#ifdef FAST_ALAW_CONVERSION - -int16_t _st_alaw2linear16[256] = { - -5504, -5248, -6016, -5760, -4480, -4224, -4992, - -4736, -7552, -7296, -8064, -7808, -6528, -6272, - -7040, -6784, -2752, -2624, -3008, -2880, -2240, - -2112, -2496, -2368, -3776, -3648, -4032, -3904, - -3264, -3136, -3520, -3392, -22016, -20992, -24064, - -23040, -17920, -16896, -19968, -18944, -30208, -29184, - -32256, -31232, -26112, -25088, -28160, -27136, -11008, - -10496, -12032, -11520, -8960, -8448, -9984, -9472, - -15104, -14592, -16128, -15616, -13056, -12544, -14080, - -13568, -344, -328, -376, -360, -280, -264, - -312, -296, -472, -456, -504, -488, -408, - -392, -440, -424, -88, -72, -120, -104, - -24, -8, -56, -40, -216, -200, -248, - -232, -152, -136, -184, -168, -1376, -1312, - -1504, -1440, -1120, -1056, -1248, -1184, -1888, - -1824, -2016, -1952, -1632, -1568, -1760, -1696, - -688, -656, -752, -720, -560, -528, -624, - -592, -944, -912, -1008, -976, -816, -784, - -880, -848, 5504, 5248, 6016, 5760, 4480, - 4224, 4992, 4736, 7552, 7296, 8064, 7808, - 6528, 6272, 7040, 6784, 2752, 2624, 3008, - 2880, 2240, 2112, 2496, 2368, 3776, 3648, - 4032, 3904, 3264, 3136, 3520, 3392, 22016, - 20992, 24064, 23040, 17920, 16896, 19968, 18944, - 30208, 29184, 32256, 31232, 26112, 25088, 28160, - 27136, 11008, 10496, 12032, 11520, 8960, 8448, - 9984, 9472, 15104, 14592, 16128, 15616, 13056, - 12544, 14080, 13568, 344, 328, 376, 360, - 280, 264, 312, 296, 472, 456, 504, - 488, 408, 392, 440, 424, 88, 72, - 120, 104, 24, 8, 56, 40, 216, - 200, 248, 232, 152, 136, 184, 168, - 1376, 1312, 1504, 1440, 1120, 1056, 1248, - 1184, 1888, 1824, 2016, 1952, 1632, 1568, - 1760, 1696, 688, 656, 752, 720, 560, - 528, 624, 592, 944, 912, 1008, 976, - 816, 784, 880, 848 -}; - -uint8_t _st_13linear2alaw[0x2000] = { - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x3a, 0x3a, 0x3a, 0x3a, - 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, - 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, - 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, - 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, - 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, - 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, - 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, - 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, - 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, - 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, - 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, - 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, - 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, - 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, - 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, - 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, - 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, - 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, - 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, - 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, - 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3c, 0x3c, 0x3c, 0x3c, - 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, - 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, - 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, - 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, - 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, - 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, - 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, - 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, - 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, - 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, - 0x3d, 0x3d, 0x3d, 0x3d, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x33, - 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, - 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, - 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, - 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, - 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, - 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, - 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, - 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, - 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, - 0x37, 0x37, 0x37, 0x37, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, - 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, - 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, - 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, - 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, - 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1a, 0x1a, 0x1a, 0x1a, - 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, - 0x1d, 0x1d, 0x1d, 0x1d, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, - 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x16, 0x16, 0x16, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, - 0x6b, 0x6b, 0x6b, 0x6b, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, - 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x6e, 0x6e, 0x6e, 0x6e, - 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, - 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, - 0x6d, 0x6d, 0x6d, 0x6d, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x60, 0x60, 0x60, 0x60, - 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, - 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, - 0x67, 0x67, 0x67, 0x67, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, - 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x7a, 0x7a, 0x7a, 0x7a, - 0x7b, 0x7b, 0x7b, 0x7b, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79, - 0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7c, 0x7c, 0x7c, 0x7c, - 0x7d, 0x7d, 0x7d, 0x7d, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73, - 0x70, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x71, 0x76, 0x76, 0x76, 0x76, - 0x77, 0x77, 0x77, 0x77, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, - 0x4a, 0x4a, 0x4b, 0x4b, 0x48, 0x48, 0x49, 0x49, 0x4e, 0x4e, 0x4f, 0x4f, - 0x4c, 0x4c, 0x4d, 0x4d, 0x42, 0x42, 0x43, 0x43, 0x40, 0x40, 0x41, 0x41, - 0x46, 0x46, 0x47, 0x47, 0x44, 0x44, 0x45, 0x45, 0x5a, 0x5a, 0x5b, 0x5b, - 0x58, 0x58, 0x59, 0x59, 0x5e, 0x5e, 0x5f, 0x5f, 0x5c, 0x5c, 0x5d, 0x5d, - 0x52, 0x52, 0x53, 0x53, 0x50, 0x50, 0x51, 0x51, 0x56, 0x56, 0x57, 0x57, - 0x54, 0x54, 0x55, 0x55, 0xd5, 0xd5, 0xd4, 0xd4, 0xd7, 0xd7, 0xd6, 0xd6, - 0xd1, 0xd1, 0xd0, 0xd0, 0xd3, 0xd3, 0xd2, 0xd2, 0xdd, 0xdd, 0xdc, 0xdc, - 0xdf, 0xdf, 0xde, 0xde, 0xd9, 0xd9, 0xd8, 0xd8, 0xdb, 0xdb, 0xda, 0xda, - 0xc5, 0xc5, 0xc4, 0xc4, 0xc7, 0xc7, 0xc6, 0xc6, 0xc1, 0xc1, 0xc0, 0xc0, - 0xc3, 0xc3, 0xc2, 0xc2, 0xcd, 0xcd, 0xcc, 0xcc, 0xcf, 0xcf, 0xce, 0xce, - 0xc9, 0xc9, 0xc8, 0xc8, 0xcb, 0xcb, 0xca, 0xca, 0xf5, 0xf5, 0xf5, 0xf5, - 0xf4, 0xf4, 0xf4, 0xf4, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6, - 0xf1, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xf0, 0xf3, 0xf3, 0xf3, 0xf3, - 0xf2, 0xf2, 0xf2, 0xf2, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xf9, 0xf9, 0xf9, 0xf9, - 0xf8, 0xf8, 0xf8, 0xf8, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, - 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, - 0xe4, 0xe4, 0xe4, 0xe4, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, - 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe1, 0xe1, 0xe1, 0xe1, - 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, - 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, - 0xe2, 0xe2, 0xe2, 0xe2, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, - 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xef, 0xef, 0xef, 0xef, - 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, - 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, - 0xe8, 0xe8, 0xe8, 0xe8, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, - 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0xb5, 0xb5, 0xb5, 0xb5, - 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, - 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, - 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, - 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, - 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, - 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, - 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, - 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, - 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, - 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, - 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, - 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, - 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, - 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, - 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, - 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, - 0xb2, 0xb2, 0xb2, 0xb2, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, - 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, - 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, - 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, - 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, - 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc, - 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, - 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, - 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, - 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, - 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, - 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, - 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, - 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, - 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, - 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, - 0xbf, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xb9, 0xb9, 0xb9, 0xb9, - 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, - 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, - 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, - 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, - 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, - 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, - 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, - 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, - 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, - 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, - 0xb8, 0xb8, 0xb8, 0xb8, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, 0xba, 0xba, 0xba, - 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, - 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, - 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, - 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, - 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa -}; - -#endif /* FAST_ALAW_CONVERSION */ - -#ifdef FAST_ULAW_CONVERSION - -int16_t _st_ulaw2linear16[256] = { - -32124, -31100, -30076, -29052, -28028, -27004, -25980, - -24956, -23932, -22908, -21884, -20860, -19836, -18812, - -17788, -16764, -15996, -15484, -14972, -14460, -13948, - -13436, -12924, -12412, -11900, -11388, -10876, -10364, - -9852, -9340, -8828, -8316, -7932, -7676, -7420, - -7164, -6908, -6652, -6396, -6140, -5884, -5628, - -5372, -5116, -4860, -4604, -4348, -4092, -3900, - -3772, -3644, -3516, -3388, -3260, -3132, -3004, - -2876, -2748, -2620, -2492, -2364, -2236, -2108, - -1980, -1884, -1820, -1756, -1692, -1628, -1564, - -1500, -1436, -1372, -1308, -1244, -1180, -1116, - -1052, -988, -924, -876, -844, -812, -780, - -748, -716, -684, -652, -620, -588, -556, - -524, -492, -460, -428, -396, -372, -356, - -340, -324, -308, -292, -276, -260, -244, - -228, -212, -196, -180, -164, -148, -132, - -120, -112, -104, -96, -88, -80, -72, - -64, -56, -48, -40, -32, -24, -16, - -8, 0, 32124, 31100, 30076, 29052, 28028, - 27004, 25980, 24956, 23932, 22908, 21884, 20860, - 19836, 18812, 17788, 16764, 15996, 15484, 14972, - 14460, 13948, 13436, 12924, 12412, 11900, 11388, - 10876, 10364, 9852, 9340, 8828, 8316, 7932, - 7676, 7420, 7164, 6908, 6652, 6396, 6140, - 5884, 5628, 5372, 5116, 4860, 4604, 4348, - 4092, 3900, 3772, 3644, 3516, 3388, 3260, - 3132, 3004, 2876, 2748, 2620, 2492, 2364, - 2236, 2108, 1980, 1884, 1820, 1756, 1692, - 1628, 1564, 1500, 1436, 1372, 1308, 1244, - 1180, 1116, 1052, 988, 924, 876, 844, - 812, 780, 748, 716, 684, 652, 620, - 588, 556, 524, 492, 460, 428, 396, - 372, 356, 340, 324, 308, 292, 276, - 260, 244, 228, 212, 196, 180, 164, - 148, 132, 120, 112, 104, 96, 88, - 80, 72, 64, 56, 48, 40, 32, - 24, 16, 8, 0 -}; - -uint8_t _st_14linear2ulaw[0x4000] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, - 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, - 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, - 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, - 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, - 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, - 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, - 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, - 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, - 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, - 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a, - 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, - 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, - 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, - 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, - 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, - 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, - 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, - 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, - 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, - 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, - 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, - 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, - 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, - 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, - 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, - 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, - 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, - 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, - 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, - 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, - 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, - 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, - 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, - 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x34, - 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, - 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, - 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, - 0x35, 0x35, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, - 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, - 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, - 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, - 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, - 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, - 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, - 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, - 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x3a, 0x3a, - 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, - 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, - 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, - 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, - 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, - 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, - 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, - 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, - 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, - 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, - 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, - 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, - 0x43, 0x43, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, - 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46, - 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, - 0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, - 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, - 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, - 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, - 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, - 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, - 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, - 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, - 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, - 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, - 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f, - 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, - 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, - 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, - 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, - 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57, - 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, - 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5a, 0x5a, - 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, - 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, - 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, - 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60, - 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x62, 0x63, 0x63, - 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66, - 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, - 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c, - 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, - 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74, - 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a, - 0x7b, 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0xff, 0xfe, 0xfe, 0xfd, - 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf7, - 0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1, - 0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xed, - 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xea, - 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7, - 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, - 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, 0xe1, - 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, - 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdd, - 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, - 0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xda, - 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, - 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7, - 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, - 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd4, - 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, - 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1, - 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, - 0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, - 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, 0xce, 0xce, 0xce, 0xce, - 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcd, - 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, - 0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, - 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, - 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xca, - 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, - 0xca, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, - 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, - 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc7, - 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, - 0xc7, 0xc7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, - 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc4, - 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, - 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, - 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, - 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1, - 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, - 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, - 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, - 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, - 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbd, - 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, - 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, - 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, - 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, - 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, - 0xbc, 0xbc, 0xbc, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, - 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, - 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, - 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, - 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, - 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, - 0xb9, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, - 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, - 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb7, - 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, - 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, - 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, - 0xb6, 0xb6, 0xb6, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, - 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, - 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb4, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, - 0xb3, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb1, - 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, - 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, - 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, - 0xb0, 0xb0, 0xb0, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80 -}; - -#endif /* FAST_ULAW_CONVERSION */ - -/* The following code was used to generate the lookup tables */ -#if 0 -int main() -{ - int x, y, find2a = 0; - - y = 0; - printf("int16_t _st_alaw2linear16[256] = {\n "); - for (x = 0; x < 256; x++) - { - printf("%8d,", st_alaw2linear16(x)); - y++; - if (y == 7) - { - y = 0; - printf("\n "); - } - } - - printf("\n};\n\nuint8_t _st_13linear2alaw[0x2000] = {\n "); - y = 0; - for (x = 0; x < 0x2000; x++) - { - printf(" 0x%02x,", st_13linear2alaw((-0x1000)+x)); - y++; - if (y == 12) - { - y = 0; - printf("\n "); - } - } - - printf("\n};\n\nint16_t _st_ulaw2linear16[256] = {\n "); - y = 0; - for (x = 0; x < 256; x++) - { - printf("%8d,", st_ulaw2linear16(x)); - y++; - if (y == 7) - { - y = 0; - printf("\n "); - } - } - - printf("\n};\n\nuint8_t _st_14linear2ulaw[0x4000] = {\n "); - y = 0; - for (x = 0; x < 0x4000; x++) - { - printf(" 0x%02x,", st_14linear2ulaw((-0x2000)+x)); - y++; - if (y == 12) - { - y = 0; - printf("\n "); - } - } - printf("\n};\n"); - -} -#endif - -/* The following is not used by SoX but kept for reference */ -#if 0 -/* copy from CCITT G.711 specifications */ -unsigned char _u2a[128] = { /* u- to A-law conversions */ - 1, 1, 2, 2, 3, 3, 4, 4, - 5, 5, 6, 6, 7, 7, 8, 8, - 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, - 25, 27, 29, 31, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, - 46, 48, 49, 50, 51, 52, 53, 54, - 55, 56, 57, 58, 59, 60, 61, 62, - 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, -/* corrected: - 81, 82, 83, 84, 85, 86, 87, 88, - should be: */ - 80, 82, 83, 84, 85, 86, 87, 88, - 89, 90, 91, 92, 93, 94, 95, 96, - 97, 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, 111, 112, - 113, 114, 115, 116, 117, 118, 119, 120, - 121, 122, 123, 124, 125, 126, 127, 128}; - -unsigned char _a2u[128] = { /* A- to u-law conversions */ - 1, 3, 5, 7, 9, 11, 13, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 32, 33, 33, 34, 34, 35, 35, - 36, 37, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 47, 48, 48, 49, 49, - 50, 51, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, 62, 63, 64, 64, - 65, 66, 67, 68, 69, 70, 71, 72, -/* corrected: - 73, 74, 75, 76, 77, 78, 79, 79, - should be: */ - 73, 74, 75, 76, 77, 78, 79, 80, - - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127}; - -/* A-law to u-law conversion */ -unsigned char st_alaw2ulaw( - unsigned char aval) -{ - aval &= 0xff; - return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) : - (0x7F ^ _a2u[aval ^ 0x55])); -} - -/* u-law to A-law conversion */ -unsigned char st_ulaw2alaw( - unsigned char uval) -{ - uval &= 0xff; - return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) : - (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1))); -} -#endif diff --git a/polyp/g711.h b/polyp/g711.h deleted file mode 100644 index 97cedf81..00000000 --- a/polyp/g711.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef foog711hfoo -#define foog711hfoo - -/* g711.h - include for G711 u-law and a-law conversion routines -** -** Copyright (C) 2001 Chris Bagwell -** -** Permission to use, copy, modify, and distribute this software and its -** documentation for any purpose and without fee is hereby granted, provided -** that the above copyright notice appear in all copies and that both that -** copyright notice and this permission notice appear in supporting -** documentation. This software is provided "as is" without express or -** implied warranty. -*/ - -/** Copied from sox -- Lennart Poettring*/ - -#include - -#ifdef FAST_ALAW_CONVERSION -extern uint8_t _st_13linear2alaw[0x2000]; -extern int16_t _st_alaw2linear16[256]; -#define st_13linear2alaw(sw) (_st_13linear2alaw[(sw + 0x1000)]) -#define st_alaw2linear16(uc) (_st_alaw2linear16[uc]) -#else -unsigned char st_13linear2alaw(int16_t pcm_val); -int16_t st_alaw2linear16(unsigned char); -#endif - -#ifdef FAST_ULAW_CONVERSION -extern uint8_t _st_14linear2ulaw[0x4000]; -extern int16_t _st_ulaw2linear16[256]; -#define st_14linear2ulaw(sw) (_st_14linear2ulaw[(sw + 0x2000)]) -#define st_ulaw2linear16(uc) (_st_ulaw2linear16[uc]) -#else -unsigned char st_14linear2ulaw(int16_t pcm_val); -int16_t st_ulaw2linear16(unsigned char); -#endif - -#endif diff --git a/polyp/gccmacro.h b/polyp/gccmacro.h deleted file mode 100644 index 9e212f2e..00000000 --- a/polyp/gccmacro.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef foogccmacrohfoo -#define foogccmacrohfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef __GNUC__ -#define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b))) -#else -/** If we're in GNU C, use some magic for detecting invalid format strings */ -#define PA_GCC_PRINTF_ATTR(a,b) -#endif - -#if defined(__GNUC__) && (__GNUC__ >= 4) -#define PA_GCC_SENTINEL __attribute__ ((sentinel)) -#else -/** Macro for usage of GCC's sentinel compilation warnings */ -#define PA_GCC_SENTINEL -#endif - -#ifdef __GNUC__ -#define PA_GCC_NORETURN __attribute__((noreturn)) -#else -/** Macro for no-return functions */ -#define PA_GCC_NORETURN -#endif - -#ifdef __GNUC__ -#define PA_GCC_UNUSED __attribute__ ((unused)) -#else -/** Macro for not used parameter */ -#define PA_GCC_UNUSED -#endif - -#endif diff --git a/polyp/glib-mainloop.c b/polyp/glib-mainloop.c deleted file mode 100644 index 6f7c1dc5..00000000 --- a/polyp/glib-mainloop.c +++ /dev/null @@ -1,538 +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 -#endif - -#include - -#include "glib-mainloop.h" -#include "idxset.h" -#include "xmalloc.h" -#include "glib.h" -#include "util.h" - -struct pa_io_event { - pa_glib_mainloop *mainloop; - int dead; - GIOChannel *io_channel; - GSource *source; - GIOCondition io_condition; - int fd; - void (*callback) (pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata); - void *userdata; - void (*destroy_callback) (pa_mainloop_api *m, pa_io_event *e, void *userdata); - pa_io_event *next, *prev; -}; - -struct pa_time_event { - pa_glib_mainloop *mainloop; - int dead; - GSource *source; - struct timeval timeval; - void (*callback) (pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata); - void *userdata; - void (*destroy_callback) (pa_mainloop_api *m, pa_time_event*e, void *userdata); - pa_time_event *next, *prev; -}; - -struct pa_defer_event { - pa_glib_mainloop *mainloop; - int dead; - GSource *source; - void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata); - void *userdata; - void (*destroy_callback) (pa_mainloop_api *m, pa_defer_event*e, void *userdata); - pa_defer_event *next, *prev; -}; - -struct pa_glib_mainloop { - GMainContext *glib_main_context; - pa_mainloop_api api; - GSource *cleanup_source; - pa_io_event *io_events, *dead_io_events; - pa_time_event *time_events, *dead_time_events; - pa_defer_event *defer_events, *dead_defer_events; -}; - -static void schedule_free_dead_events(pa_glib_mainloop *g); - -static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f); - -static pa_io_event* glib_io_new(pa_mainloop_api*m, int fd, pa_io_event_flags_t f, void (*callback) (pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata), void *userdata) { - pa_io_event *e; - pa_glib_mainloop *g; - - assert(m && m->userdata && fd >= 0 && callback); - g = m->userdata; - - e = pa_xmalloc(sizeof(pa_io_event)); - e->mainloop = m->userdata; - e->dead = 0; - e->fd = fd; - e->callback = callback; - e->userdata = userdata; - e->destroy_callback = NULL; - - e->io_channel = g_io_channel_unix_new(e->fd); - assert(e->io_channel); - e->source = NULL; - e->io_condition = 0; - - glib_io_enable(e, f); - - e->next = g->io_events; - if (e->next) e->next->prev = e; - g->io_events = e; - e->prev = NULL; - - return e; -} - -/* The callback GLIB calls whenever an IO condition is met */ -static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) { - pa_io_event *e = data; - pa_io_event_flags_t f; - assert(source && e && e->io_channel == source); - - f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) | - (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) | - (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) | - (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0); - - e->callback(&e->mainloop->api, e, e->fd, f, e->userdata); - return TRUE; -} - -static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) { - GIOCondition c; - assert(e && !e->dead); - - c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0); - - if (c == e->io_condition) - return; - - if (e->source) { - g_source_destroy(e->source); - g_source_unref(e->source); - } - - e->source = g_io_create_watch(e->io_channel, c | G_IO_ERR | G_IO_HUP); - assert(e->source); - - g_source_set_callback(e->source, (GSourceFunc) io_cb, e, NULL); - g_source_attach(e->source, e->mainloop->glib_main_context); - g_source_set_priority(e->source, G_PRIORITY_DEFAULT); - - e->io_condition = c; -} - -static void glib_io_free(pa_io_event*e) { - assert(e && !e->dead); - - if (e->source) { - g_source_destroy(e->source); - g_source_unref(e->source); - e->source = NULL; - } - - if (e->prev) - e->prev->next = e->next; - else - e->mainloop->io_events = e->next; - - if (e->next) - e->next->prev = e->prev; - - if ((e->next = e->mainloop->dead_io_events)) - e->next->prev = e; - - e->mainloop->dead_io_events = e; - e->prev = NULL; - - e->dead = 1; - schedule_free_dead_events(e->mainloop); -} - -static void glib_io_set_destroy(pa_io_event*e, void (*callback)(pa_mainloop_api*m, pa_io_event *e, void *userdata)) { - assert(e); - e->destroy_callback = callback; -} - -/* Time sources */ - -static void glib_time_restart(pa_time_event*e, const struct timeval *tv); - -static pa_time_event* glib_time_new(pa_mainloop_api*m, const struct timeval *tv, void (*callback) (pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) { - pa_glib_mainloop *g; - pa_time_event *e; - - assert(m && m->userdata && tv && callback); - g = m->userdata; - - e = pa_xmalloc(sizeof(pa_time_event)); - e->mainloop = g; - e->dead = 0; - e->callback = callback; - e->userdata = userdata; - e->destroy_callback = NULL; - e->source = NULL; - - glib_time_restart(e, tv); - - e->next = g->time_events; - if (e->next) e->next->prev = e; - g->time_events = e; - e->prev = NULL; - - return e; -} - -static guint msec_diff(const struct timeval *a, const struct timeval *b) { - guint r; - assert(a && b); - - if (a->tv_sec < b->tv_sec) - return 0; - - if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec) - return 0; - - r = (a->tv_sec-b->tv_sec)*1000; - - if (a->tv_usec >= b->tv_usec) - r += (a->tv_usec - b->tv_usec) / 1000; - else - r -= (b->tv_usec - a->tv_usec) / 1000; - - return r; -} - -static gboolean time_cb(gpointer data) { - pa_time_event* e = data; - assert(e && e->mainloop && e->source); - - g_source_unref(e->source); - e->source = NULL; - - e->callback(&e->mainloop->api, e, &e->timeval, e->userdata); - return FALSE; -} - -static void glib_time_restart(pa_time_event*e, const struct timeval *tv) { - struct timeval now; - assert(e && e->mainloop && !e->dead); - - pa_gettimeofday(&now); - if (e->source) { - g_source_destroy(e->source); - g_source_unref(e->source); - } - - if (tv) { - e->timeval = *tv; - e->source = g_timeout_source_new(msec_diff(tv, &now)); - assert(e->source); - g_source_set_callback(e->source, time_cb, e, NULL); - g_source_set_priority(e->source, G_PRIORITY_DEFAULT); - g_source_attach(e->source, e->mainloop->glib_main_context); - } else - e->source = NULL; - } - -static void glib_time_free(pa_time_event *e) { - assert(e && e->mainloop && !e->dead); - - if (e->source) { - g_source_destroy(e->source); - g_source_unref(e->source); - e->source = NULL; - } - - if (e->prev) - e->prev->next = e->next; - else - e->mainloop->time_events = e->next; - - if (e->next) - e->next->prev = e->prev; - - if ((e->next = e->mainloop->dead_time_events)) - e->next->prev = e; - - e->mainloop->dead_time_events = e; - e->prev = NULL; - - e->dead = 1; - schedule_free_dead_events(e->mainloop); -} - -static void glib_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*m, pa_time_event*e, void *userdata)) { - assert(e); - e->destroy_callback = callback; -} - -/* Deferred sources */ - -static void glib_defer_enable(pa_defer_event *e, int b); - -static pa_defer_event* glib_defer_new(pa_mainloop_api*m, void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata), void *userdata) { - pa_defer_event *e; - pa_glib_mainloop *g; - - assert(m && m->userdata && callback); - g = m->userdata; - - e = pa_xmalloc(sizeof(pa_defer_event)); - e->mainloop = g; - e->dead = 0; - e->callback = callback; - e->userdata = userdata; - e->destroy_callback = NULL; - e->source = NULL; - - glib_defer_enable(e, 1); - - e->next = g->defer_events; - if (e->next) e->next->prev = e; - g->defer_events = e; - e->prev = NULL; - return e; -} - -static gboolean idle_cb(gpointer data) { - pa_defer_event* e = data; - assert(e && e->mainloop && e->source); - - e->callback(&e->mainloop->api, e, e->userdata); - return TRUE; -} - -static void glib_defer_enable(pa_defer_event *e, int b) { - assert(e && e->mainloop); - - if (e->source && !b) { - g_source_destroy(e->source); - g_source_unref(e->source); - e->source = NULL; - } else if (!e->source && b) { - e->source = g_idle_source_new(); - assert(e->source); - g_source_set_callback(e->source, idle_cb, e, NULL); - g_source_attach(e->source, e->mainloop->glib_main_context); - g_source_set_priority(e->source, G_PRIORITY_HIGH); - } -} - -static void glib_defer_free(pa_defer_event *e) { - assert(e && e->mainloop && !e->dead); - - if (e->source) { - g_source_destroy(e->source); - g_source_unref(e->source); - e->source = NULL; - } - - if (e->prev) - e->prev->next = e->next; - else - e->mainloop->defer_events = e->next; - - if (e->next) - e->next->prev = e->prev; - - if ((e->next = e->mainloop->dead_defer_events)) - e->next->prev = e; - - e->mainloop->dead_defer_events = e; - e->prev = NULL; - - e->dead = 1; - schedule_free_dead_events(e->mainloop); -} - -static void glib_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api *m, pa_defer_event *e, void *userdata)) { - assert(e); - e->destroy_callback = callback; -} - -/* quit() */ - -static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) { - pa_glib_mainloop *g; - assert(a && a->userdata); - g = a->userdata; - - /* NOOP */ -} - -static const pa_mainloop_api vtable = { - .userdata = NULL, - - .io_new = glib_io_new, - .io_enable = glib_io_enable, - .io_free = glib_io_free, - .io_set_destroy= glib_io_set_destroy, - - .time_new = glib_time_new, - .time_restart = glib_time_restart, - .time_free = glib_time_free, - .time_set_destroy = glib_time_set_destroy, - - .defer_new = glib_defer_new, - .defer_enable = glib_defer_enable, - .defer_free = glib_defer_free, - .defer_set_destroy = glib_defer_set_destroy, - - .quit = glib_quit, -}; - -pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) { - pa_glib_mainloop *g; - - g = pa_xmalloc(sizeof(pa_glib_mainloop)); - if (c) { - g->glib_main_context = c; - g_main_context_ref(c); - } else - g->glib_main_context = g_main_context_default(); - - g->api = vtable; - g->api.userdata = g; - - g->io_events = g->dead_io_events = NULL; - g->time_events = g->dead_time_events = NULL; - g->defer_events = g->dead_defer_events = NULL; - - g->cleanup_source = NULL; - return g; -} - -static void free_io_events(pa_io_event *e) { - while (e) { - pa_io_event *r = e; - e = r->next; - - if (r->source) { - g_source_destroy(r->source); - g_source_unref(r->source); - } - - if (r->io_channel) - g_io_channel_unref(r->io_channel); - - if (r->destroy_callback) - r->destroy_callback(&r->mainloop->api, r, r->userdata); - - pa_xfree(r); - } -} - -static void free_time_events(pa_time_event *e) { - while (e) { - pa_time_event *r = e; - e = r->next; - - if (r->source) { - g_source_destroy(r->source); - g_source_unref(r->source); - } - - if (r->destroy_callback) - r->destroy_callback(&r->mainloop->api, r, r->userdata); - - pa_xfree(r); - } -} - -static void free_defer_events(pa_defer_event *e) { - while (e) { - pa_defer_event *r = e; - e = r->next; - - if (r->source) { - g_source_destroy(r->source); - g_source_unref(r->source); - } - - if (r->destroy_callback) - r->destroy_callback(&r->mainloop->api, r, r->userdata); - - pa_xfree(r); - } -} - -void pa_glib_mainloop_free(pa_glib_mainloop* g) { - assert(g); - - free_io_events(g->io_events); - free_io_events(g->dead_io_events); - free_defer_events(g->defer_events); - free_defer_events(g->dead_defer_events); - free_time_events(g->time_events); - free_time_events(g->dead_time_events); - - if (g->cleanup_source) { - g_source_destroy(g->cleanup_source); - g_source_unref(g->cleanup_source); - } - - g_main_context_unref(g->glib_main_context); - pa_xfree(g); -} - -pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) { - assert(g); - return &g->api; -} - -static gboolean free_dead_events(gpointer p) { - pa_glib_mainloop *g = p; - assert(g); - - free_io_events(g->dead_io_events); - free_defer_events(g->dead_defer_events); - free_time_events(g->dead_time_events); - - g->dead_io_events = NULL; - g->dead_defer_events = NULL; - g->dead_time_events = NULL; - - g_source_destroy(g->cleanup_source); - g_source_unref(g->cleanup_source); - g->cleanup_source = NULL; - - return FALSE; -} - -static void schedule_free_dead_events(pa_glib_mainloop *g) { - assert(g && g->glib_main_context); - - if (g->cleanup_source) - return; - - g->cleanup_source = g_idle_source_new(); - assert(g->cleanup_source); - g_source_set_callback(g->cleanup_source, free_dead_events, g, NULL); - g_source_attach(g->cleanup_source, g->glib_main_context); -} diff --git a/polyp/glib-mainloop.h b/polyp/glib-mainloop.h deleted file mode 100644 index d3e98597..00000000 --- a/polyp/glib-mainloop.h +++ /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 - -#include "mainloop-api.h" -#include "cdecl.h" - -/** \file - * GLIB main loop support */ - -PA_C_DECL_BEGIN - -/** \pa_glib_mainloop - * An opaque GLIB main loop object */ -typedef struct pa_glib_mainloop pa_glib_mainloop; - -/** Create a new GLIB main loop object for the specified GLIB main loop context. If c is NULL the default context is used. */ -#if GLIB_MAJOR_VERSION >= 2 -pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c); -#else -pa_glib_mainloop *pa_glib_mainloop_new(void); -#endif - - -/** Free the GLIB main loop object */ -void pa_glib_mainloop_free(pa_glib_mainloop* g); - -/** Return the abstract main loop API vtable for the GLIB main loop object */ -pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g); - -PA_C_DECL_END - -#endif diff --git a/polyp/glib12-mainloop.c b/polyp/glib12-mainloop.c deleted file mode 100644 index 8bc4483b..00000000 --- a/polyp/glib12-mainloop.c +++ /dev/null @@ -1,500 +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 -#endif - -#include - -#include "glib-mainloop.h" -#include "idxset.h" -#include "xmalloc.h" -#include "util.h" - -/* A mainloop implementation based on GLIB 1.2 */ - -struct pa_io_event { - pa_glib_mainloop *mainloop; - int dead; - GIOChannel *io_channel; - guint source; - GIOCondition io_condition; - int fd; - void (*callback) (pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata); - void *userdata; - void (*destroy_callback) (pa_mainloop_api *m, pa_io_event*e, void *userdata); - pa_io_event *next, *prev; -}; - -struct pa_time_event { - pa_glib_mainloop *mainloop; - int dead; - guint source; - struct timeval timeval; - void (*callback) (pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata); - void *userdata; - void (*destroy_callback) (pa_mainloop_api *m, pa_time_event*e, void *userdata); - pa_time_event *next, *prev; -}; - -struct pa_defer_event { - pa_glib_mainloop *mainloop; - int dead; - guint source; - void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata); - void *userdata; - void (*destroy_callback) (pa_mainloop_api *m, pa_defer_event*e, void *userdata); - pa_defer_event *next, *prev; -}; - -struct pa_glib_mainloop { - pa_mainloop_api api; - guint cleanup_source; - pa_io_event *io_events, *dead_io_events; - pa_time_event *time_events, *dead_time_events; - pa_defer_event *defer_events, *dead_defer_events; -}; - -static void schedule_free_dead_events(pa_glib_mainloop *g); - -static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f); - -static pa_io_event* glib_io_new(pa_mainloop_api*m, int fd, pa_io_event_flags_t f, void (*callback) (pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata), void *userdata) { - pa_io_event *e; - pa_glib_mainloop *g; - - assert(m && m->userdata && fd >= 0 && callback); - g = m->userdata; - - e = pa_xmalloc(sizeof(pa_io_event)); - e->mainloop = m->userdata; - e->dead = 0; - e->fd = fd; - e->callback = callback; - e->userdata = userdata; - e->destroy_callback = NULL; - - e->io_channel = g_io_channel_unix_new(e->fd); - assert(e->io_channel); - e->source = (guint) -1; - e->io_condition = 0; - - glib_io_enable(e, f); - - e->next = g->io_events; - if (e->next) e->next->prev = e; - g->io_events = e; - e->prev = NULL; - - return e; -} - -static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) { - pa_io_event *e = data; - pa_io_event_flags_t f; - assert(source && e && e->io_channel == source); - - f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) | - (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) | - (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) | - (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0); - - e->callback(&e->mainloop->api, e, e->fd, f, e->userdata); - return TRUE; -} - -static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) { - GIOCondition c; - assert(e && !e->dead); - - c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0); - - if (c == e->io_condition) - return; - - if (e->source != (guint) -1) - g_source_remove(e->source); - - e->source = g_io_add_watch_full(e->io_channel, G_PRIORITY_DEFAULT, c | G_IO_ERR | G_IO_HUP, io_cb, e, NULL); - assert(e->source != (guint) -1); - e->io_condition = c; -} - -static void glib_io_free(pa_io_event*e) { - assert(e && !e->dead); - - if (e->source != (guint) -1) { - g_source_remove(e->source); - e->source = (guint) -1; - } - - if (e->prev) - e->prev->next = e->next; - else - e->mainloop->io_events = e->next; - - if (e->next) - e->next->prev = e->prev; - - if ((e->next = e->mainloop->dead_io_events)) - e->next->prev = e; - - e->mainloop->dead_io_events = e; - e->prev = NULL; - - e->dead = 1; - schedule_free_dead_events(e->mainloop); -} - -static void glib_io_set_destroy(pa_io_event*e, void (*callback)(pa_mainloop_api*m, pa_io_event *e, void *userdata)) { - assert(e); - e->destroy_callback = callback; -} - -/* Time sources */ - -static void glib_time_restart(pa_time_event*e, const struct timeval *tv); - -static pa_time_event* glib_time_new(pa_mainloop_api*m, const struct timeval *tv, void (*callback) (pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) { - pa_glib_mainloop *g; - pa_time_event *e; - - assert(m && m->userdata && tv && callback); - g = m->userdata; - - e = pa_xmalloc(sizeof(pa_time_event)); - e->mainloop = g; - e->dead = 0; - e->callback = callback; - e->userdata = userdata; - e->destroy_callback = NULL; - e->source = (guint) -1; - - glib_time_restart(e, tv); - - e->next = g->time_events; - if (e->next) e->next->prev = e; - g->time_events = e; - e->prev = NULL; - - return e; -} - -static guint msec_diff(const struct timeval *a, const struct timeval *b) { - guint r; - assert(a && b); - - if (a->tv_sec < b->tv_sec) - return 0; - - if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec) - return 0; - - r = (a->tv_sec-b->tv_sec)*1000; - - if (a->tv_usec >= b->tv_usec) - r += (a->tv_usec - b->tv_usec) / 1000; - else - r -= (b->tv_usec - a->tv_usec) / 1000; - - return r; -} - -static gboolean time_cb(gpointer data) { - pa_time_event* e = data; - assert(e && e->mainloop && e->source != (guint) -1); - - g_source_remove(e->source); - e->source = (guint) -1; - - e->callback(&e->mainloop->api, e, &e->timeval, e->userdata); - return FALSE; -} - -static void glib_time_restart(pa_time_event*e, const struct timeval *tv) { - struct timeval now; - assert(e && e->mainloop && !e->dead); - - pa_gettimeofday(&now); - if (e->source != (guint) -1) - g_source_remove(e->source); - - if (tv) { - e->timeval = *tv; - e->source = g_timeout_add_full(G_PRIORITY_DEFAULT, msec_diff(tv, &now), time_cb, e, NULL); - assert(e->source != (guint) -1); - } else - e->source = (guint) -1; - } - -static void glib_time_free(pa_time_event *e) { - assert(e && e->mainloop && !e->dead); - - if (e->source != (guint) -1) { - g_source_remove(e->source); - e->source = (guint) -1; - } - - if (e->prev) - e->prev->next = e->next; - else - e->mainloop->time_events = e->next; - - if (e->next) - e->next->prev = e->prev; - - if ((e->next = e->mainloop->dead_time_events)) - e->next->prev = e; - - e->mainloop->dead_time_events = e; - e->prev = NULL; - - e->dead = 1; - schedule_free_dead_events(e->mainloop); -} - -static void glib_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*m, pa_time_event*e, void *userdata)) { - assert(e); - e->destroy_callback = callback; -} - -/* Deferred sources */ - -static void glib_defer_enable(pa_defer_event *e, int b); - -static pa_defer_event* glib_defer_new(pa_mainloop_api*m, void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata), void *userdata) { - pa_defer_event *e; - pa_glib_mainloop *g; - - assert(m && m->userdata && callback); - g = m->userdata; - - e = pa_xmalloc(sizeof(pa_defer_event)); - e->mainloop = g; - e->dead = 0; - e->callback = callback; - e->userdata = userdata; - e->destroy_callback = NULL; - e->source = (guint) -1; - - glib_defer_enable(e, 1); - - e->next = g->defer_events; - if (e->next) e->next->prev = e; - g->defer_events = e; - e->prev = NULL; - return e; -} - -static gboolean idle_cb(gpointer data) { - pa_defer_event* e = data; - assert(e && e->mainloop && e->source != (guint) -1); - - e->callback(&e->mainloop->api, e, e->userdata); - return TRUE; -} - -static void glib_defer_enable(pa_defer_event *e, int b) { - assert(e && e->mainloop); - - if (e->source != (guint) -1 && !b) { - g_source_remove(e->source); - e->source = (guint) -1; - } else if (e->source == (guint) -1 && b) { - e->source = g_idle_add_full(G_PRIORITY_HIGH, idle_cb, e, NULL); - assert(e->source != (guint) -1); - } -} - -static void glib_defer_free(pa_defer_event *e) { - assert(e && e->mainloop && !e->dead); - - if (e->source != (guint) -1) { - g_source_remove(e->source); - e->source = (guint) -1; - } - - if (e->prev) - e->prev->next = e->next; - else - e->mainloop->defer_events = e->next; - - if (e->next) - e->next->prev = e->prev; - - if ((e->next = e->mainloop->dead_defer_events)) - e->next->prev = e; - - e->mainloop->dead_defer_events = e; - e->prev = NULL; - - e->dead = 1; - schedule_free_dead_events(e->mainloop); -} - -static void glib_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api *m, pa_defer_event *e, void *userdata)) { - assert(e); - e->destroy_callback = callback; -} - -/* quit() */ - -static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) { - pa_glib_mainloop *g; - assert(a && a->userdata); - g = a->userdata; - - /* NOOP */ -} - -static const pa_mainloop_api vtable = { - .userdata = NULL, - - .io_new = glib_io_new, - .io_enable = glib_io_enable, - .io_free = glib_io_free, - .io_set_destroy= glib_io_set_destroy, - - .time_new = glib_time_new, - .time_restart = glib_time_restart, - .time_free = glib_time_free, - .time_set_destroy = glib_time_set_destroy, - - .defer_new = glib_defer_new, - .defer_enable = glib_defer_enable, - .defer_free = glib_defer_free, - .defer_set_destroy = glib_defer_set_destroy, - - .quit = glib_quit, -}; - -pa_glib_mainloop *pa_glib_mainloop_new(void) { - pa_glib_mainloop *g; - - g = pa_xmalloc(sizeof(pa_glib_mainloop)); - - g->api = vtable; - g->api.userdata = g; - - g->io_events = g->dead_io_events = NULL; - g->time_events = g->dead_time_events = NULL; - g->defer_events = g->dead_defer_events = NULL; - - g->cleanup_source = (guint) -1; - return g; -} - -static void free_io_events(pa_io_event *e) { - while (e) { - pa_io_event *r = e; - e = r->next; - - if (r->source != (guint) -1) - g_source_remove(r->source); - - if (r->io_channel) - g_io_channel_unref(r->io_channel); - - if (r->destroy_callback) - r->destroy_callback(&r->mainloop->api, r, r->userdata); - - pa_xfree(r); - } -} - -static void free_time_events(pa_time_event *e) { - while (e) { - pa_time_event *r = e; - e = r->next; - - if (r->source != (guint) -1) - g_source_remove(r->source); - - if (r->destroy_callback) - r->destroy_callback(&r->mainloop->api, r, r->userdata); - - pa_xfree(r); - } -} - -static void free_defer_events(pa_defer_event *e) { - while (e) { - pa_defer_event *r = e; - e = r->next; - - if (r->source != (guint) -1) - g_source_remove(r->source); - - if (r->destroy_callback) - r->destroy_callback(&r->mainloop->api, r, r->userdata); - - pa_xfree(r); - } -} - -void pa_glib_mainloop_free(pa_glib_mainloop* g) { - assert(g); - - free_io_events(g->io_events); - free_io_events(g->dead_io_events); - free_defer_events(g->defer_events); - free_defer_events(g->dead_defer_events); - free_time_events(g->time_events); - free_time_events(g->dead_time_events); - - if (g->cleanup_source != (guint) -1) - g_source_remove(g->cleanup_source); - - pa_xfree(g); -} - -pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) { - assert(g); - return &g->api; -} - -static gboolean free_dead_events(gpointer p) { - pa_glib_mainloop *g = p; - assert(g); - - free_io_events(g->dead_io_events); - free_defer_events(g->dead_defer_events); - free_time_events(g->dead_time_events); - - g->dead_io_events = NULL; - g->dead_defer_events = NULL; - g->dead_time_events = NULL; - - g_source_remove(g->cleanup_source); - g->cleanup_source = (guint) -1; - - return FALSE; -} - -static void schedule_free_dead_events(pa_glib_mainloop *g) { - assert(g); - - if (g->cleanup_source != (guint) -1) - return; - - g->cleanup_source = g_idle_add_full(G_PRIORITY_HIGH, free_dead_events, g, NULL); -} diff --git a/polyp/hashmap.c b/polyp/hashmap.c deleted file mode 100644 index a37decb8..00000000 --- a/polyp/hashmap.c +++ /dev/null @@ -1,194 +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 -#endif - -#include -#include -#include - -#include "hashmap.h" -#include "idxset.h" -#include "xmalloc.h" -#include "log.h" - -#define BUCKETS 1023 - -struct hashmap_entry { - struct hashmap_entry *next, *previous, *bucket_next, *bucket_previous; - unsigned hash; - const void *key; - void *value; -}; - -struct pa_hashmap { - unsigned size; - struct hashmap_entry **data; - struct hashmap_entry *first_entry; - - unsigned n_entries; - unsigned (*hash_func) (const void *p); - int (*compare_func) (const void*a, const void*b); -}; - -pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) { - pa_hashmap *h; - h = pa_xmalloc(sizeof(pa_hashmap)); - h->data = pa_xmalloc0(sizeof(struct hashmap_entry*)*(h->size = BUCKETS)); - h->first_entry = NULL; - h->n_entries = 0; - h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func; - h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func; - return h; -} - -static void remove(pa_hashmap *h, struct hashmap_entry *e) { - assert(e); - - if (e->next) - e->next->previous = e->previous; - if (e->previous) - e->previous->next = e->next; - else - h->first_entry = e->next; - - if (e->bucket_next) - e->bucket_next->bucket_previous = e->bucket_previous; - if (e->bucket_previous) - e->bucket_previous->bucket_next = e->bucket_next; - else { - assert(e->hash < h->size); - h->data[e->hash] = e->bucket_next; - } - - pa_xfree(e); - h->n_entries--; -} - -void pa_hashmap_free(pa_hashmap*h, void (*free_func)(void *p, void *userdata), void *userdata) { - assert(h); - - while (h->first_entry) { - if (free_func) - free_func(h->first_entry->value, userdata); - remove(h, h->first_entry); - } - - pa_xfree(h->data); - pa_xfree(h); -} - -static struct hashmap_entry *get(pa_hashmap *h, unsigned hash, const void *key) { - struct hashmap_entry *e; - assert(h && hash < h->size); - - for (e = h->data[hash]; e; e = e->bucket_next) - if (h->compare_func(e->key, key) == 0) - return e; - - return NULL; -} - -int pa_hashmap_put(pa_hashmap *h, const void *key, void *value) { - struct hashmap_entry *e; - unsigned hash; - assert(h); - - hash = h->hash_func(key) % h->size; - - if ((e = get(h, hash, key))) - return -1; - - e = pa_xmalloc(sizeof(struct hashmap_entry)); - e->hash = hash; - e->key = key; - e->value = value; - - e->previous = NULL; - e->next = h->first_entry; - if (h->first_entry) - h->first_entry->previous = e; - h->first_entry = e; - - e->bucket_previous = NULL; - e->bucket_next = h->data[hash]; - if (h->data[hash]) - h->data[hash]->bucket_previous = e; - h->data[hash] = e; - - h->n_entries ++; - return 0; -} - -void* pa_hashmap_get(pa_hashmap *h, const void *key) { - unsigned hash; - struct hashmap_entry *e; - assert(h && key); - - hash = h->hash_func(key) % h->size; - - if (!(e = get(h, hash, key))) - return NULL; - - return e->value; -} - -void* pa_hashmap_remove(pa_hashmap *h, const void *key) { - struct hashmap_entry *e; - unsigned hash; - void *data; - assert(h && key); - - hash = h->hash_func(key) % h->size; - - if (!(e = get(h, hash, key))) - return NULL; - - data = e->value; - remove(h, e); - return data; -} - -unsigned pa_hashmap_size(pa_hashmap *h) { - return h->n_entries; -} - -void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) { - assert(h && state); - - if (!*state) - *state = h->first_entry; - else - *state = ((struct hashmap_entry*) *state)->next; - - if (!*state) { - if (key) - *key = NULL; - return NULL; - } - - if (key) - *key = ((struct hashmap_entry*) *state)->key; - - return ((struct hashmap_entry*) *state)->value; -} diff --git a/polyp/hashmap.h b/polyp/hashmap.h deleted file mode 100644 index 14f82705..00000000 --- a/polyp/hashmap.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef foohashmaphfoo -#define foohashmaphfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -/* Simple Implementation of a hash table. Memory management is the - * user's job. It's a good idea to have the key pointer point to a - * string in the value data. */ - -typedef struct pa_hashmap pa_hashmap; - -/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map */ -pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)); - -/* Free the hash table. Calls the specified function for every value in the table. The function may be NULL */ -void pa_hashmap_free(pa_hashmap*, void (*free_func)(void *p, void *userdata), void *userdata); - -/* Returns non-zero when the entry already exists */ -int pa_hashmap_put(pa_hashmap *h, const void *key, void *value); -void* pa_hashmap_get(pa_hashmap *h, const void *key); - -/* Returns the data of the entry while removing */ -void* pa_hashmap_remove(pa_hashmap *h, const void *key); - -unsigned pa_hashmap_size(pa_hashmap *h); - -/* May be used to iterate through the hashmap. Initially the opaque - pointer *state has to be set to NULL. The hashmap may not be - modified during iteration. The key of the entry is returned in - *key, if key is non-NULL. After the last entry in the hashmap NULL - is returned. */ -void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key); - -#endif diff --git a/polyp/howl-wrap.c b/polyp/howl-wrap.c deleted file mode 100644 index 77d096ac..00000000 --- a/polyp/howl-wrap.c +++ /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 - -#include "howl-wrap.h" -#include "log.h" -#include "xmalloc.h" -#include "props.h" - -#define HOWL_PROPERTY "howl" - -pa_howl_wrapper { - pa_core *core; - int ref; - - pa_io_event *io_event; - sw_discovery discovery; - -}; - -static void howl_io_event(pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags f, void *userdata) { - pa_howl_wrapper *w = userdata; - assert(m && e && fd >= 0 && w && w->ref >= 1); - - if (f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) - goto fail; - - if (sw_discovery_read_socket(w->discovery) != SW_OKAY) - goto fail; - - return; - -fail: - pa_log(__FILE__": howl connection died.\n"); - w->core->mainloop->io_free(w->io_event); - w->io_event = NULL; -} - -static pa_howl_wrapper* howl_wrapper_new(pa_core *c) { - pa_howl_wrapper *h; - sw_discovery session; - assert(c); - - if (sw_discovery_init(&session) != SW_OKAY) { - pa_log("sw_discovery_init() failed.\n"); - return NULL; - } - - h = pa_xmalloc(sizeof(pa_howl_wrapper)); - h->core = c; - h->ref = 1; - h->discovery = session; - - h->io_event = c->mainloop->io_new(c->mainloop, sw_discovery_socket(session), PA_IO_EVENT_INPUT, howl_io_event, h); - - return h; -} - -static void howl_wrapper_free(pa_howl_wrapper *h) { - assert(h); - - sw_discovery_fina(h->discovery); - - if (h->io_event) - h->core->mainloop->io_free(h->io_event); - - pa_xfree(h); -} - -pa_howl_wrapper* pa_howl_wrapper_get(pa_core *c) { - pa_howl_wrapper *h; - assert(c); - - if ((h = pa_property_get(c, HOWL_PROPERTY))) - return pa_howl_wrapper_ref(h); - - return howl_wrapper_new(c); -} - -pa_howl_wrapper* pa_howl_wrapper_ref(pa_howl_wrapper *h) { - assert(h && h->ref >= 1); - h->ref++; - return h; -} - -void pa_howl_wrapper_unref(pa_howl_wrapper *h) { - assert(h && h->ref >= 1); - if (!(--h->ref)) - howl_wrapper_free(h); -} - -sw_discovery pa_howl_wrapper_get_discovery(pa_howl_wrapper *h) { - assert(h && h->ref >= 1); - - return h->discovery; -} - diff --git a/polyp/howl-wrap.h b/polyp/howl-wrap.h deleted file mode 100644 index a670b082..00000000 --- a/polyp/howl-wrap.h +++ /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 - -#include "core.h" - -pa_howl_wrapper; - -pa_howl_wrapper* pa_howl_wrapper_get(pa_core *c); -pa_howl_wrapper* pa_howl_wrapper_ref(pa_howl_wrapper *h); -void pa_howl_wrapper_unref(pa_howl_wrapper *h); - -sw_discovery pa_howl_wrapper_get_discovery(pa_howl_wrapper *h); - -#endif diff --git a/polyp/idxset.c b/polyp/idxset.c deleted file mode 100644 index 409d1fab..00000000 --- a/polyp/idxset.c +++ /dev/null @@ -1,390 +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 -#endif - -#include -#include -#include -#include - -#include "idxset.h" -#include "xmalloc.h" - -typedef struct idxset_entry { - void *data; - uint32_t index; - unsigned hash_value; - - struct idxset_entry *hash_prev, *hash_next; - struct idxset_entry* iterate_prev, *iterate_next; -} idxset_entry; - -struct pa_idxset { - unsigned (*hash_func) (const void *p); - int (*compare_func)(const void *a, const void *b); - - unsigned hash_table_size, n_entries; - idxset_entry **hash_table, **array, *iterate_list_head, *iterate_list_tail; - uint32_t index, start_index, array_size; -}; - -unsigned pa_idxset_string_hash_func(const void *p) { - unsigned hash = 0; - const char *c; - - for (c = p; *c; c++) - hash = 31 * hash + *c; - - return hash; -} - -int pa_idxset_string_compare_func(const void *a, const void *b) { - return strcmp(a, b); -} - -unsigned pa_idxset_trivial_hash_func(const void *p) { - return (unsigned) p; -} - -int pa_idxset_trivial_compare_func(const void *a, const void *b) { - return a != b; -} - -pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) { - pa_idxset *s; - - s = pa_xnew(pa_idxset, 1); - s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func; - s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func; - s->hash_table_size = 1023; - s->hash_table = pa_xmalloc0(sizeof(idxset_entry*)*s->hash_table_size); - s->array = NULL; - s->array_size = 0; - s->index = 0; - s->start_index = 0; - s->n_entries = 0; - - s->iterate_list_head = s->iterate_list_tail = NULL; - - return s; -} - -void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata) { - assert(s); - - while (s->iterate_list_head) { - idxset_entry *e = s->iterate_list_head; - s->iterate_list_head = s->iterate_list_head->iterate_next; - - if (free_func) - free_func(e->data, userdata); - pa_xfree(e); - } - - pa_xfree(s->hash_table); - pa_xfree(s->array); - pa_xfree(s); -} - -static idxset_entry* hash_scan(pa_idxset *s, idxset_entry* e, const void *p) { - assert(p); - - assert(s->compare_func); - for (; e; e = e->hash_next) - if (s->compare_func(e->data, p) == 0) - return e; - - return NULL; -} - -static void extend_array(pa_idxset *s, uint32_t idx) { - uint32_t i, j, l; - idxset_entry** n; - assert(idx >= s->start_index); - - if (idx < s->start_index + s->array_size) - return; - - for (i = 0; i < s->array_size; i++) - if (s->array[i]) - break; - - l = idx - s->start_index - i + 100; - n = pa_xnew0(idxset_entry*, l); - - for (j = 0; j < s->array_size-i; j++) - n[j] = s->array[i+j]; - - pa_xfree(s->array); - - s->array = n; - s->array_size = l; - s->start_index += i; -} - -static idxset_entry** array_index(pa_idxset*s, uint32_t idx) { - if (idx >= s->start_index + s->array_size) - return NULL; - - if (idx < s->start_index) - return NULL; - - return s->array + (idx - s->start_index); -} - -int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) { - unsigned h; - idxset_entry *e, **a; - assert(s && p); - - assert(s->hash_func); - h = s->hash_func(p) % s->hash_table_size; - - assert(s->hash_table); - if ((e = hash_scan(s, s->hash_table[h], p))) { - if (idx) - *idx = e->index; - - return -1; - } - - e = pa_xmalloc(sizeof(idxset_entry)); - e->data = p; - e->index = s->index++; - e->hash_value = h; - - /* Insert into hash table */ - e->hash_next = s->hash_table[h]; - e->hash_prev = NULL; - if (s->hash_table[h]) - s->hash_table[h]->hash_prev = e; - s->hash_table[h] = e; - - /* Insert into array */ - extend_array(s, e->index); - a = array_index(s, e->index); - assert(a && !*a); - *a = e; - - /* Insert into linked list */ - e->iterate_next = NULL; - e->iterate_prev = s->iterate_list_tail; - if (s->iterate_list_tail) { - assert(s->iterate_list_head); - s->iterate_list_tail->iterate_next = e; - } else { - assert(!s->iterate_list_head); - s->iterate_list_head = e; - } - s->iterate_list_tail = e; - - s->n_entries++; - assert(s->n_entries >= 1); - - if (idx) - *idx = e->index; - - return 0; -} - -void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) { - idxset_entry **a; - assert(s); - - if (!(a = array_index(s, idx))) - return NULL; - - if (!*a) - return NULL; - - return (*a)->data; -} - -void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) { - unsigned h; - idxset_entry *e; - assert(s && p); - - assert(s->hash_func); - h = s->hash_func(p) % s->hash_table_size; - - assert(s->hash_table); - if (!(e = hash_scan(s, s->hash_table[h], p))) - return NULL; - - if (idx) - *idx = e->index; - - return e->data; -} - -static void remove_entry(pa_idxset *s, idxset_entry *e) { - idxset_entry **a; - assert(s && e); - - /* Remove from array */ - a = array_index(s, e->index); - assert(a && *a && *a == e); - *a = NULL; - - /* Remove from linked list */ - if (e->iterate_next) - e->iterate_next->iterate_prev = e->iterate_prev; - else - s->iterate_list_tail = e->iterate_prev; - - if (e->iterate_prev) - e->iterate_prev->iterate_next = e->iterate_next; - else - s->iterate_list_head = e->iterate_next; - - /* Remove from hash table */ - if (e->hash_next) - e->hash_next->hash_prev = e->hash_prev; - - if (e->hash_prev) - e->hash_prev->hash_next = e->hash_next; - else - s->hash_table[e->hash_value] = e->hash_next; - - pa_xfree(e); - - assert(s->n_entries >= 1); - s->n_entries--; -} - -void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) { - idxset_entry **a; - void *data; - - assert(s); - - if (!(a = array_index(s, idx))) - return NULL; - - data = (*a)->data; - remove_entry(s, *a); - - return data; -} - -void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) { - idxset_entry *e; - unsigned h; - void *r; - - assert(s->hash_func); - h = s->hash_func(data) % s->hash_table_size; - - assert(s->hash_table); - if (!(e = hash_scan(s, s->hash_table[h], data))) - return NULL; - - r = e->data; - if (idx) - *idx = e->index; - - remove_entry(s, e); - - return r; -} - -void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) { - idxset_entry **a, *e = NULL; - assert(s && idx); - - if ((a = array_index(s, *idx)) && *a) - e = (*a)->iterate_next; - - if (!e) - e = s->iterate_list_head; - - if (!e) - return NULL; - - *idx = e->index; - return e->data; -} - -void* pa_idxset_first(pa_idxset *s, uint32_t *idx) { - assert(s); - - if (!s->iterate_list_head) - return NULL; - - if (idx) - *idx = s->iterate_list_head->index; - return s->iterate_list_head->data; -} - -void *pa_idxset_next(pa_idxset *s, uint32_t *idx) { - idxset_entry **a, *e = NULL; - assert(s && idx); - - if ((a = array_index(s, *idx)) && *a) - e = (*a)->iterate_next; - - if (e) { - *idx = e->index; - return e->data; - } else { - *idx = PA_IDXSET_INVALID; - return NULL; - } -} - - -int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata) { - idxset_entry *e; - assert(s && func); - - e = s->iterate_list_head; - while (e) { - int del = 0, r; - idxset_entry *n = e->iterate_next; - - r = func(e->data, e->index, &del, userdata); - - if (del) - remove_entry(s, e); - - if (r < 0) - return r; - - e = n; - } - - return 0; -} - -unsigned pa_idxset_size(pa_idxset*s) { - assert(s); - return s->n_entries; -} - -int pa_idxset_isempty(pa_idxset *s) { - assert(s); - return s->n_entries == 0; -} - diff --git a/polyp/idxset.h b/polyp/idxset.h deleted file mode 100644 index 17ae16cb..00000000 --- a/polyp/idxset.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef fooidxsethfoo -#define fooidxsethfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -/* A combination of a set and a dynamic array. Entries are indexable - * both through a numeric automatically generated index and the entry's - * data pointer. As usual, memory management is the user's job. */ - -/* A special index value denoting the invalid index. */ -#define PA_IDXSET_INVALID ((uint32_t) -1) - -/* Generic implementations for hash and comparison functions. Just - * compares the pointer or calculates the hash value directly from the - * pointer value. */ -unsigned pa_idxset_trivial_hash_func(const void *p); -int pa_idxset_trivial_compare_func(const void *a, const void *b); - -/* Generic implementations for hash and comparison functions for strings. */ -unsigned pa_idxset_string_hash_func(const void *p); -int pa_idxset_string_compare_func(const void *a, const void *b); - -typedef struct pa_idxset pa_idxset; - -/* Instantiate a new idxset with the specified hash and comparison functions */ -pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)); - -/* Free the idxset. When the idxset is not empty the specified function is called for every entry contained */ -void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata); - -/* Store a new item in the idxset. The index of the item is returned in *idx */ -int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx); - -/* Get the entry by its idx */ -void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx); - -/* Get the entry by its data. The idx is returned in *index */ -void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx); - -/* Similar to pa_idxset_get_by_index(), but removes the entry from the idxset. */ -void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx); - -/* Similar to pa_idxset_get_by_data(), but removes the entry from the idxset */ -void* pa_idxset_remove_by_data(pa_idxset*s, const void *p, uint32_t *idx); - -/* This may be used to iterate through all entries. When called with - an invalid index value it returns the first entry, otherwise the - next following. The function is best called with *idx = - PA_IDXSET_VALID first. It is safe to manipulate the idxset between - the calls. It is not guaranteed that all entries have already been - returned before the an entry is returned the second time.*/ -void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx); - -/* Return the oldest entry in the idxset. Fill in its index in *idx. */ -void* pa_idxset_first(pa_idxset *s, uint32_t *idx); - -/* Return the entry following the entry indexed by *idx. After the - * call *index contains the index of the returned - * object. pa_idxset_first() and pa_idxset_next() may be used to - * iterate through the set.*/ -void *pa_idxset_next(pa_idxset *s, uint32_t *idx); - -/* Call a function for every item in the set. If the callback function - returns -1, the loop is terminated. If *del is set to non-zero that - specific item is removed. It is not safe to call any other - functions on the idxset while pa_idxset_foreach is executed. */ -int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata); - -unsigned pa_idxset_size(pa_idxset*s); - -int pa_idxset_isempty(pa_idxset *s); - -#endif diff --git a/polyp/inet_ntop.c b/polyp/inet_ntop.c deleted file mode 100644 index a25c3c95..00000000 --- a/polyp/inet_ntop.c +++ /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.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 -#endif - -#include -#include -#include - -#ifndef HAVE_INET_NTOP - -#ifdef HAVE_SYS_SOCKET_H -#include -#endif - -#include "winsock.h" - -#include "inet_ntop.h" - -const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) { - struct in_addr *in = (struct in_addr*)src; - struct in6_addr *in6 = (struct in6_addr*)src; - - assert(src && dst); - - switch (af) { - case AF_INET: - snprintf(dst, cnt, "%d.%d.%d.%d", -#ifdef WORDS_BIGENDIAN - (int)(in->s_addr >> 24) & 0xff, - (int)(in->s_addr >> 16) & 0xff, - (int)(in->s_addr >> 8) & 0xff, - (int)(in->s_addr >> 0) & 0xff); -#else - (int)(in->s_addr >> 0) & 0xff, - (int)(in->s_addr >> 8) & 0xff, - (int)(in->s_addr >> 16) & 0xff, - (int)(in->s_addr >> 24) & 0xff); -#endif - break; - case AF_INET6: - snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x", - in6->s6_addr[ 0] << 8 | in6->s6_addr[ 1], - in6->s6_addr[ 2] << 8 | in6->s6_addr[ 3], - in6->s6_addr[ 4] << 8 | in6->s6_addr[ 5], - in6->s6_addr[ 6] << 8 | in6->s6_addr[ 7], - in6->s6_addr[ 8] << 8 | in6->s6_addr[ 9], - in6->s6_addr[10] << 8 | in6->s6_addr[11], - in6->s6_addr[12] << 8 | in6->s6_addr[13], - in6->s6_addr[14] << 8 | in6->s6_addr[15]); - break; - default: - errno = EAFNOSUPPORT; - return NULL; - } - - return dst; -} - -#endif /* INET_NTOP */ diff --git a/polyp/inet_ntop.h b/polyp/inet_ntop.h deleted file mode 100644 index 7fb67b44..00000000 --- a/polyp/inet_ntop.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef fooinet_ntophfoo -#define fooinet_ntophfoo - -#ifdef HAVE_SYS_SOCKET_H -#include -#endif - -#include "winsock.h" - -const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt); - -#endif diff --git a/polyp/iochannel.c b/polyp/iochannel.c deleted file mode 100644 index 273d47e0..00000000 --- a/polyp/iochannel.c +++ /dev/null @@ -1,292 +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 -#endif - -#include -#include -#include -#include - -#include "winsock.h" - -#include "iochannel.h" -#include "util.h" -#include "socket-util.h" -#include "xmalloc.h" - -struct pa_iochannel { - int ifd, ofd; - pa_mainloop_api* mainloop; - - pa_iochannel_callback_t callback; - void*userdata; - - int readable; - int writable; - int hungup; - - int no_close; - - pa_io_event* input_event, *output_event; -}; - -static void enable_mainloop_sources(pa_iochannel *io) { - assert(io); - - if (io->input_event == io->output_event && io->input_event) { - pa_io_event_flags_t f = PA_IO_EVENT_NULL; - assert(io->input_event); - - if (!io->readable) - f |= PA_IO_EVENT_INPUT; - if (!io->writable) - f |= PA_IO_EVENT_OUTPUT; - - io->mainloop->io_enable(io->input_event, f); - } else { - if (io->input_event) - io->mainloop->io_enable(io->input_event, io->readable ? PA_IO_EVENT_NULL : PA_IO_EVENT_INPUT); - if (io->output_event) - io->mainloop->io_enable(io->output_event, io->writable ? PA_IO_EVENT_NULL : PA_IO_EVENT_OUTPUT); - } -} - -static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) { - pa_iochannel *io = userdata; - int changed = 0; - - assert(m); - assert(e); - assert(fd >= 0); - assert(userdata); - - if ((f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) && !io->hungup) { - io->hungup = 1; - changed = 1; - - if (e == io->input_event) { - io->mainloop->io_free(io->input_event); - io->input_event = NULL; - - if (io->output_event == e) - io->output_event = NULL; - } else if (e == io->output_event) { - io->mainloop->io_free(io->output_event); - io->output_event = NULL; - } - } else { - - if ((f & PA_IO_EVENT_INPUT) && !io->readable) { - io->readable = 1; - changed = 1; - assert(e == io->input_event); - } - - if ((f & PA_IO_EVENT_OUTPUT) && !io->writable) { - io->writable = 1; - changed = 1; - assert(e == io->output_event); - } - } - - if (changed) { - enable_mainloop_sources(io); - - if (io->callback) - io->callback(io, io->userdata); - } -} - -pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) { - pa_iochannel *io; - - assert(m); - assert(ifd >= 0 || ofd >= 0); - - io = pa_xnew(pa_iochannel, 1); - io->ifd = ifd; - io->ofd = ofd; - io->mainloop = m; - - io->userdata = NULL; - io->callback = NULL; - io->readable = 0; - io->writable = 0; - io->hungup = 0; - io->no_close = 0; - - io->input_event = io->output_event = NULL; - - if (ifd == ofd) { - assert(ifd >= 0); - pa_make_nonblock_fd(io->ifd); - io->input_event = io->output_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT|PA_IO_EVENT_OUTPUT, callback, io); - } else { - - if (ifd >= 0) { - pa_make_nonblock_fd(io->ifd); - io->input_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT, callback, io); - } - - if (ofd >= 0) { - pa_make_nonblock_fd(io->ofd); - io->output_event = m->io_new(m, ofd, PA_IO_EVENT_OUTPUT, callback, io); - } - } - - return io; -} - -void pa_iochannel_free(pa_iochannel*io) { - assert(io); - - if (io->input_event) - io->mainloop->io_free(io->input_event); - - if (io->output_event && (io->output_event != io->input_event)) - io->mainloop->io_free(io->output_event); - - if (!io->no_close) { - if (io->ifd >= 0) - - close(io->ifd); - if (io->ofd >= 0 && io->ofd != io->ifd) - close(io->ofd); - } - - pa_xfree(io); -} - -int pa_iochannel_is_readable(pa_iochannel*io) { - assert(io); - - return io->readable || io->hungup; -} - -int pa_iochannel_is_writable(pa_iochannel*io) { - assert(io); - - return io->writable && !io->hungup; -} - -int pa_iochannel_is_hungup(pa_iochannel*io) { - assert(io); - - return io->hungup; -} - -ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) { - ssize_t r; - - assert(io); - assert(data); - assert(l); - assert(io->ofd >= 0); - -#ifdef OS_IS_WIN32 - r = send(io->ofd, data, l, 0); - if (r < 0) { - if (WSAGetLastError() != WSAENOTSOCK) { - errno = WSAGetLastError(); - return r; - } - } - - if (r < 0) -#endif - r = write(io->ofd, data, l); - if (r >= 0) { - io->writable = 0; - enable_mainloop_sources(io); - } - - return r; -} - -ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) { - ssize_t r; - - assert(io); - assert(data); - assert(io->ifd >= 0); - -#ifdef OS_IS_WIN32 - r = recv(io->ifd, data, l, 0); - if (r < 0) { - if (WSAGetLastError() != WSAENOTSOCK) { - errno = WSAGetLastError(); - return r; - } - } - - if (r < 0) -#endif - r = read(io->ifd, data, l); - - if (r >= 0) { - io->readable = 0; - enable_mainloop_sources(io); - } - - return r; -} - -void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_callback_t _callback, void *userdata) { - assert(io); - - io->callback = _callback; - io->userdata = userdata; -} - -void pa_iochannel_set_noclose(pa_iochannel*io, int b) { - assert(io); - - io->no_close = b; -} - -void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l) { - assert(io); - assert(s); - assert(l); - - pa_socket_peer_to_string(io->ifd, s, l); -} - -int pa_iochannel_socket_set_rcvbuf(pa_iochannel *io, size_t l) { - assert(io); - - return pa_socket_set_rcvbuf(io->ifd, l); -} - -int pa_iochannel_socket_set_sndbuf(pa_iochannel *io, size_t l) { - assert(io); - - return pa_socket_set_sndbuf(io->ofd, l); -} - -pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io) { - assert(io); - - return io->mainloop; -} diff --git a/polyp/iochannel.h b/polyp/iochannel.h deleted file mode 100644 index e2b8bccf..00000000 --- a/polyp/iochannel.h +++ /dev/null @@ -1,72 +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 -#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. */ - -typedef struct pa_iochannel pa_iochannel; - -/* Create a new IO channel for the specified file descriptors for -input resp. output. It is safe to pass the same file descriptor for -both parameters (in case of full-duplex channels). For a simplex -channel specify -1 for the other direction. */ - -pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd); -void pa_iochannel_free(pa_iochannel*io); - -ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l); -ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l); - -int pa_iochannel_is_readable(pa_iochannel*io); -int pa_iochannel_is_writable(pa_iochannel*io); -int pa_iochannel_is_hungup(pa_iochannel*io); - -/* Don't close the file descirptors when the io channel is freed. By - * default the file descriptors are closed. */ -void pa_iochannel_set_noclose(pa_iochannel*io, int b); - -/* Set the callback function that is called whenever data becomes available for read or write */ -typedef void (*pa_iochannel_callback_t)(pa_iochannel*io, void *userdata); -void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_callback_t callback, void *userdata); - -/* In case the file descriptor is a socket, return a pretty-printed string in *s which describes the peer connected */ -void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l); - -/* Use setsockopt() to tune the recieve and send buffers of TCP sockets */ -int pa_iochannel_socket_set_rcvbuf(pa_iochannel*io, size_t l); -int pa_iochannel_socket_set_sndbuf(pa_iochannel*io, size_t l); - -pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io); - -#endif diff --git a/polyp/ioline.c b/polyp/ioline.c deleted file mode 100644 index 5b669f5c..00000000 --- a/polyp/ioline.c +++ /dev/null @@ -1,368 +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 -#endif - -#include -#include -#include -#include -#include - -#include "ioline.h" -#include "xmalloc.h" -#include "log.h" - -#define BUFFER_LIMIT (64*1024) -#define READ_SIZE (1024) - -struct pa_ioline { - pa_iochannel *io; - pa_defer_event *defer_event; - pa_mainloop_api *mainloop; - int ref; - int dead; - - char *wbuf; - size_t wbuf_length, wbuf_index, wbuf_valid_length; - - char *rbuf; - size_t rbuf_length, rbuf_index, rbuf_valid_length; - - void (*callback)(pa_ioline*io, const char *s, void *userdata); - void *userdata; - - int defer_close; -}; - -static void io_callback(pa_iochannel*io, void *userdata); -static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata); - -pa_ioline* pa_ioline_new(pa_iochannel *io) { - pa_ioline *l; - assert(io); - - l = pa_xmalloc(sizeof(pa_ioline)); - l->io = io; - l->dead = 0; - - l->wbuf = NULL; - l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0; - - l->rbuf = NULL; - l->rbuf_length = l->rbuf_index = l->rbuf_valid_length = 0; - - l->callback = NULL; - l->userdata = NULL; - l->ref = 1; - - l->mainloop = pa_iochannel_get_mainloop_api(io); - - l->defer_event = l->mainloop->defer_new(l->mainloop, defer_callback, l); - l->mainloop->defer_enable(l->defer_event, 0); - - l->defer_close = 0; - - pa_iochannel_set_callback(io, io_callback, l); - - return l; -} - -static void ioline_free(pa_ioline *l) { - assert(l); - - if (l->io) - pa_iochannel_free(l->io); - - if (l->defer_event) - l->mainloop->defer_free(l->defer_event); - - pa_xfree(l->wbuf); - pa_xfree(l->rbuf); - pa_xfree(l); -} - -void pa_ioline_unref(pa_ioline *l) { - assert(l && l->ref >= 1); - - if ((--l->ref) <= 0) - ioline_free(l); -} - -pa_ioline* pa_ioline_ref(pa_ioline *l) { - assert(l && l->ref >= 1); - - l->ref++; - return l; -} - -void pa_ioline_close(pa_ioline *l) { - assert(l && l->ref >= 1); - - l->dead = 1; - if (l->io) { - pa_iochannel_free(l->io); - l->io = NULL; - } - - if (l->defer_event) { - l->mainloop->defer_free(l->defer_event); - l->defer_event = NULL; - } -} - -void pa_ioline_puts(pa_ioline *l, const char *c) { - size_t len; - assert(l && c && l->ref >= 1 && !l->dead); - - pa_ioline_ref(l); - - len = strlen(c); - if (len > BUFFER_LIMIT - l->wbuf_valid_length) - len = BUFFER_LIMIT - l->wbuf_valid_length; - - if (len) { - assert(l->wbuf_length >= l->wbuf_valid_length); - - /* In case the allocated buffer is too small, enlarge it. */ - if (l->wbuf_valid_length + len > l->wbuf_length) { - size_t n = l->wbuf_valid_length+len; - char *new = pa_xmalloc(n); - if (l->wbuf) { - memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length); - pa_xfree(l->wbuf); - } - l->wbuf = new; - l->wbuf_length = n; - l->wbuf_index = 0; - } else if (l->wbuf_index + l->wbuf_valid_length + len > l->wbuf_length) { - - /* In case the allocated buffer fits, but the current index is too far from the start, move it to the front. */ - memmove(l->wbuf, l->wbuf+l->wbuf_index, l->wbuf_valid_length); - l->wbuf_index = 0; - } - - assert(l->wbuf_index + l->wbuf_valid_length + len <= l->wbuf_length); - - /* Append the new string */ - memcpy(l->wbuf + l->wbuf_index + l->wbuf_valid_length, c, len); - l->wbuf_valid_length += len; - - l->mainloop->defer_enable(l->defer_event, 1); - } - - pa_ioline_unref(l); -} - -void pa_ioline_set_callback(pa_ioline*l, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata) { - assert(l && l->ref >= 1); - l->callback = callback; - l->userdata = userdata; -} - -static void failure(pa_ioline *l) { - assert(l && l->ref >= 1 && !l->dead); - - pa_ioline_close(l); - - if (l->callback) { - l->callback(l, NULL, l->userdata); - l->callback = NULL; - } -} - -static void scan_for_lines(pa_ioline *l, size_t skip) { - assert(l && l->ref >= 1 && skip < l->rbuf_valid_length); - - while (!l->dead && l->rbuf_valid_length > skip) { - char *e, *p; - size_t m; - - if (!(e = memchr(l->rbuf + l->rbuf_index + skip, '\n', l->rbuf_valid_length - skip))) - break; - - *e = 0; - - p = l->rbuf + l->rbuf_index; - m = strlen(p); - - l->rbuf_index += m+1; - l->rbuf_valid_length -= m+1; - - /* A shortcut for the next time */ - if (l->rbuf_valid_length == 0) - l->rbuf_index = 0; - - if (l->callback) - l->callback(l, p, l->userdata); - - skip = 0; - } - - /* If the buffer became too large and still no newline was found, drop it. */ - if (l->rbuf_valid_length >= BUFFER_LIMIT) - l->rbuf_index = l->rbuf_valid_length = 0; -} - -static int do_write(pa_ioline *l); - -static int do_read(pa_ioline *l) { - assert(l && l->ref >= 1); - - while (!l->dead && pa_iochannel_is_readable(l->io)) { - ssize_t r; - size_t len; - - len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length; - - /* Check if we have to enlarge the read buffer */ - if (len < READ_SIZE) { - size_t n = l->rbuf_valid_length+READ_SIZE; - - if (n >= BUFFER_LIMIT) - n = BUFFER_LIMIT; - - if (l->rbuf_length >= n) { - /* The current buffer is large enough, let's just move the data to the front */ - if (l->rbuf_valid_length) - memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length); - } else { - /* Enlarge the buffer */ - char *new = pa_xmalloc(n); - if (l->rbuf_valid_length) - memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length); - pa_xfree(l->rbuf); - l->rbuf = new; - l->rbuf_length = n; - } - - l->rbuf_index = 0; - } - - len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length; - - assert(len >= READ_SIZE); - - /* Read some data */ - r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len); - if (r == 0) { - /* Got an EOF, so fake an exit command. */ - l->rbuf_index = 0; - snprintf (l->rbuf, l->rbuf_length, "exit\n"); - r = 5; - pa_ioline_puts(l, "\nExiting.\n"); - do_write(l); - } else if (r < 0) { - pa_log(__FILE__": read() failed: %s\n", strerror(errno)); - failure(l); - return -1; - } - - l->rbuf_valid_length += r; - - /* Look if a line has been terminated in the newly read data */ - scan_for_lines(l, l->rbuf_valid_length - r); - } - - return 0; -} - -/* Try to flush the buffer */ -static int do_write(pa_ioline *l) { - ssize_t r; - assert(l && l->ref >= 1); - - while (!l->dead && pa_iochannel_is_writable(l->io) && l->wbuf_valid_length) { - - if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0) { - pa_log(__FILE__": write() failed: %s\n", r < 0 ? strerror(errno) : "EOF"); - failure(l); - return -1; - } - - l->wbuf_index += r; - l->wbuf_valid_length -= r; - - /* A shortcut for the next time */ - if (l->wbuf_valid_length == 0) - l->wbuf_index = 0; - } - - return 0; -} - -/* Try to flush read/write data */ -static void do_work(pa_ioline *l) { - assert(l && l->ref >= 1); - - pa_ioline_ref(l); - - l->mainloop->defer_enable(l->defer_event, 0); - - if (!l->dead) - do_write(l); - - if (!l->dead) - do_read(l); - - if (l->defer_close && !l->wbuf_valid_length) - failure(l); - - pa_ioline_unref(l); -} - -static void io_callback(pa_iochannel*io, void *userdata) { - pa_ioline *l = userdata; - assert(io && l && l->ref >= 1); - - do_work(l); -} - -static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata) { - pa_ioline *l = userdata; - assert(l && l->ref >= 1 && l->mainloop == m && l->defer_event == e); - - do_work(l); -} - -void pa_ioline_defer_close(pa_ioline *l) { - assert(l); - - l->defer_close = 1; - - if (!l->wbuf_valid_length) - l->mainloop->defer_enable(l->defer_event, 1); -} - -void pa_ioline_printf(pa_ioline *s, const char *format, ...) { - char *t; - va_list ap; - - - va_start(ap, format); - t = pa_vsprintf_malloc(format, ap); - va_end(ap); - - pa_ioline_puts(s, t); - pa_xfree(t); -} diff --git a/polyp/ioline.h b/polyp/ioline.h deleted file mode 100644 index 84ccb47a..00000000 --- a/polyp/ioline.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef fooiolinehfoo -#define fooiolinehfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "iochannel.h" -#include "util.h" - -/* An ioline wraps an iochannel for line based communication. A - * callback function is called whenever a new line has been recieved - * from the client */ - -typedef struct pa_ioline pa_ioline; - -pa_ioline* pa_ioline_new(pa_iochannel *io); -void pa_ioline_unref(pa_ioline *l); -pa_ioline* pa_ioline_ref(pa_ioline *l); -void pa_ioline_close(pa_ioline *l); - -/* Write a string to the channel */ -void pa_ioline_puts(pa_ioline *s, const char *c); - -/* Write a string to the channel */ -void pa_ioline_printf(pa_ioline *s, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3); - -/* Set the callback function that is called for every recieved line */ -void pa_ioline_set_callback(pa_ioline*io, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata); - -/* Make sure to close the ioline object as soon as the send buffer is emptied */ -void pa_ioline_defer_close(pa_ioline *io); - -#endif diff --git a/polyp/llist.h b/polyp/llist.h deleted file mode 100644 index eb8cd017..00000000 --- a/polyp/llist.h +++ /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 index 97406f79..00000000 --- a/polyp/log.c +++ /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 -#endif - -#include -#include -#include - -#ifdef HAVE_SYSLOG_H -#include -#endif - -#include "log.h" -#include "xmalloc.h" -#include "util.h" - -#define ENV_LOGLEVEL "POLYP_LOG" - -static char *log_ident = NULL; -static pa_log_target_t log_target = PA_LOG_STDERR; -static void (*user_log_func)(pa_log_level_t l, const char *s) = NULL; -static pa_log_level_t maximal_level = PA_LOG_NOTICE; - -#ifdef HAVE_SYSLOG_H -static const int level_to_syslog[] = { - [PA_LOG_ERROR] = LOG_ERR, - [PA_LOG_WARN] = LOG_WARNING, - [PA_LOG_NOTICE] = LOG_NOTICE, - [PA_LOG_INFO] = LOG_INFO, - [PA_LOG_DEBUG] = LOG_DEBUG -}; -#endif - -void pa_log_set_ident(const char *p) { - if (log_ident) - pa_xfree(log_ident); - - log_ident = pa_xstrdup(p); -} - -void pa_log_set_maximal_level(pa_log_level_t l) { - assert(l < PA_LOG_LEVEL_MAX); - maximal_level = l; -} - -void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t l, const char*s)) { - assert(t == PA_LOG_USER || !func); - log_target = t; - user_log_func = func; -} - -void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) { - const char *e; - assert(level < PA_LOG_LEVEL_MAX); - - if ((e = getenv(ENV_LOGLEVEL))) - maximal_level = atoi(e); - - if (level > maximal_level) - return; - - switch (log_target) { - case PA_LOG_STDERR: - vfprintf(stderr, format, ap); - break; - -#ifdef HAVE_SYSLOG_H - case PA_LOG_SYSLOG: - openlog(log_ident ? log_ident : "???", LOG_PID, LOG_USER); - vsyslog(level_to_syslog[level], format, ap); - closelog(); - break; -#endif - - case PA_LOG_USER: { - char *t = pa_vsprintf_malloc(format, ap); - assert(user_log_func); - user_log_func(level, t); - pa_xfree(t); - } - - case PA_LOG_NULL: - default: - break; - } - -} - -void pa_log_level(pa_log_level_t level, const char *format, ...) { - va_list ap; - va_start(ap, format); - pa_log_levelv(level, format, ap); - va_end(ap); -} - -void pa_log_debug(const char *format, ...) { - va_list ap; - va_start(ap, format); - pa_log_levelv(PA_LOG_DEBUG, format, ap); - va_end(ap); -} - -void pa_log_info(const char *format, ...) { - va_list ap; - va_start(ap, format); - pa_log_levelv(PA_LOG_INFO, format, ap); - va_end(ap); -} - -void pa_log_notice(const char *format, ...) { - va_list ap; - va_start(ap, format); - pa_log_levelv(PA_LOG_INFO, format, ap); - va_end(ap); -} - -void pa_log_warn(const char *format, ...) { - va_list ap; - va_start(ap, format); - pa_log_levelv(PA_LOG_WARN, format, ap); - va_end(ap); -} - -void pa_log_error(const char *format, ...) { - va_list ap; - va_start(ap, format); - pa_log_levelv(PA_LOG_ERROR, format, ap); - va_end(ap); -} diff --git a/polyp/log.h b/polyp/log.h deleted file mode 100644 index ce8aea98..00000000 --- a/polyp/log.h +++ /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 -#include "gccmacro.h" - -/* A simple logging subsystem */ - -/* Where to log to */ -typedef enum pa_log_target { - PA_LOG_STDERR, /* default */ - PA_LOG_SYSLOG, - PA_LOG_USER, /* to user specified function */ - PA_LOG_NULL /* to /dev/null */ -} pa_log_target_t; - -typedef enum pa_log_level { - PA_LOG_ERROR = 0, /* Error messages */ - PA_LOG_WARN = 1, /* Warning messages */ - PA_LOG_NOTICE = 2, /* Notice messages */ - PA_LOG_INFO = 3, /* Info messages */ - PA_LOG_DEBUG = 4, /* debug message */ - PA_LOG_LEVEL_MAX -} pa_log_level_t; - -/* Set an identification for the current daemon. Used when logging to syslog. */ -void pa_log_set_ident(const char *p); - -/* Set another log target. If t is PA_LOG_USER you may specify a function that is called every log string */ -void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t t, const char*s)); - -/* Minimal log level */ -void pa_log_set_maximal_level(pa_log_level_t l); - -/* Do a log line */ -void pa_log_debug(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); -void pa_log_info(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); -void pa_log_notice(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); -void pa_log_warn(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); -void pa_log_error(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); - -void pa_log_level(pa_log_level_t level, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3); - -void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap); - -#define pa_log pa_log_error - -#endif diff --git a/polyp/main.c b/polyp/main.c deleted file mode 100644 index b5aac851..00000000 --- a/polyp/main.c +++ /dev/null @@ -1,472 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SYS_IOCTL_H -#include -#endif - -#ifdef HAVE_LIBWRAP -#include -#include -#endif - -#include "winsock.h" - -#include "core.h" -#include "mainloop.h" -#include "module.h" -#include "mainloop-signal.h" -#include "cmdline.h" -#include "cli-command.h" -#include "util.h" -#include "sioman.h" -#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" -#include "namereg.h" - -#ifdef HAVE_LIBWRAP -/* Only one instance of these variables */ -int allow_severity = LOG_INFO; -int deny_severity = LOG_WARNING; -#endif - -#ifdef OS_IS_WIN32 - -static void message_cb(pa_mainloop_api*a, pa_defer_event *e, void *userdata) { - MSG msg; - - while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - if (msg.message == WM_QUIT) - raise(SIGTERM); - else { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } -} - -#endif - -static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e, int sig, void *userdata) { - pa_log_info(__FILE__": Got signal %s.\n", pa_strsignal(sig)); - - switch (sig) { -#ifdef SIGUSR1 - case SIGUSR1: - pa_module_load(userdata, "module-cli", NULL); - break; -#endif - -#ifdef SIGUSR2 - case SIGUSR2: - pa_module_load(userdata, "module-cli-protocol-unix", NULL); - break; -#endif - -#ifdef SIGHUP - case SIGHUP: { - char *c = pa_full_status_string(userdata); - pa_log_notice(c); - pa_xfree(c); - return; - } -#endif - - case SIGINT: - case SIGTERM: - default: - pa_log_info(__FILE__": Exiting.\n"); - m->quit(m, 1); - break; - } -} - -static void close_pipe(int p[2]) { - if (p[0] != -1) - close(p[0]); - if (p[1] != -1) - close(p[1]); - p[0] = p[1] = -1; -} - -int main(int argc, char *argv[]) { - pa_core *c; - pa_strbuf *buf = NULL; - pa_daemon_conf *conf; - pa_mainloop *mainloop; - - char *s; - int r, retval = 1, d = 0; - int daemon_pipe[2] = { -1, -1 }; - int suid_root; - int valid_pid_file = 0; - -#ifdef HAVE_GETUID - gid_t gid = (gid_t) -1; -#endif - -#ifdef OS_IS_WIN32 - pa_defer_event *defer; -#endif - - pa_limit_caps(); - -#ifdef HAVE_GETUID - suid_root = getuid() != 0 && geteuid() == 0; - - if (suid_root && (pa_uid_in_group("realtime", &gid) <= 0 || gid >= 1000)) { - pa_log_warn(__FILE__": WARNING: called SUID root, but not in group 'realtime'.\n"); - pa_drop_root(); - } -#else - suid_root = 0; -#endif - - LTDL_SET_PRELOADED_SYMBOLS(); - - r = lt_dlinit(); - assert(r == 0); - -#ifdef OS_IS_WIN32 - { - WSADATA data; - WSAStartup(MAKEWORD(2, 0), &data); - } -#endif - - pa_log_set_ident("polypaudio"); - - conf = pa_daemon_conf_new(); - - if (pa_daemon_conf_load(conf, NULL) < 0) - goto finish; - - if (pa_daemon_conf_env(conf) < 0) - goto finish; - - if (pa_cmdline_parse(conf, argc, argv, &d) < 0) { - pa_log(__FILE__": failed to parse command line.\n"); - goto finish; - } - - pa_log_set_maximal_level(conf->log_level); - pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL); - - if (conf->high_priority && conf->cmd == PA_CMD_DAEMON) - pa_raise_priority(); - - pa_drop_caps(); - - if (suid_root) - pa_drop_root(); - - if (conf->dl_search_path) - lt_dlsetsearchpath(conf->dl_search_path); - - switch (conf->cmd) { - case PA_CMD_DUMP_MODULES: - pa_dump_modules(conf, argc-d, argv+d); - retval = 0; - goto finish; - - case PA_CMD_DUMP_CONF: { - s = pa_daemon_conf_dump(conf); - fputs(s, stdout); - pa_xfree(s); - retval = 0; - goto finish; - } - - case PA_CMD_HELP : - pa_cmdline_help(argv[0]); - retval = 0; - goto finish; - - case PA_CMD_VERSION : - printf(PACKAGE_NAME" "PACKAGE_VERSION"\n"); - retval = 0; - goto finish; - - case PA_CMD_CHECK: { - pid_t pid; - - if (pa_pid_file_check_running(&pid) < 0) { - pa_log_info(__FILE__": daemon not running\n"); - } else { - pa_log_info(__FILE__": daemon running as PID %u\n", pid); - retval = 0; - } - - goto finish; - - } - case PA_CMD_KILL: - - if (pa_pid_file_kill(SIGINT, NULL) < 0) - pa_log(__FILE__": failed to kill daemon.\n"); - else - retval = 0; - - goto finish; - - default: - assert(conf->cmd == PA_CMD_DAEMON); - } - - if (conf->daemonize) { - pid_t child; - int tty_fd; - - if (pa_stdio_acquire() < 0) { - pa_log(__FILE__": failed to acquire stdio.\n"); - goto finish; - } - -#ifdef HAVE_FORK - if (pipe(daemon_pipe) < 0) { - pa_log(__FILE__": failed to create pipe.\n"); - goto finish; - } - - if ((child = fork()) < 0) { - pa_log(__FILE__": fork() failed: %s\n", strerror(errno)); - goto finish; - } - - if (child != 0) { - /* Father */ - - close(daemon_pipe[1]); - daemon_pipe[1] = -1; - - if (pa_loop_read(daemon_pipe[0], &retval, sizeof(retval)) != sizeof(retval)) { - pa_log(__FILE__": read() failed: %s\n", strerror(errno)); - retval = 1; - } - - if (retval) - pa_log(__FILE__": daemon startup failed.\n"); - else - pa_log_info(__FILE__": daemon startup successful.\n"); - - goto finish; - } - - close(daemon_pipe[0]); - daemon_pipe[0] = -1; -#endif - - if (conf->auto_log_target) - pa_log_set_target(PA_LOG_SYSLOG, NULL); - -#ifdef HAVE_SETSID - setsid(); -#endif -#ifdef HAVE_SETPGID - setpgid(0,0); -#endif - -#ifndef OS_IS_WIN32 - close(0); - close(1); - close(2); - - open("/dev/null", O_RDONLY); - open("/dev/null", O_WRONLY); - open("/dev/null", O_WRONLY); -#else - FreeConsole(); -#endif - -#ifdef SIGTTOU - signal(SIGTTOU, SIG_IGN); -#endif -#ifdef SIGTTIN - signal(SIGTTIN, SIG_IGN); -#endif -#ifdef SIGTSTP - signal(SIGTSTP, SIG_IGN); -#endif - -#ifdef TIOCNOTTY - if ((tty_fd = open("/dev/tty", O_RDWR)) >= 0) { - ioctl(tty_fd, TIOCNOTTY, (char*) 0); - close(tty_fd); - } -#endif - } - - chdir("/"); - - if (conf->use_pid_file) { - if (pa_pid_file_create() < 0) { - pa_log(__FILE__": pa_pid_file_create() failed.\n"); -#ifdef HAVE_FORK - if (conf->daemonize) - pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); -#endif - goto finish; - } - - valid_pid_file = 1; - } - - mainloop = pa_mainloop_new(); - assert(mainloop); - - c = pa_core_new(pa_mainloop_get_api(mainloop)); - assert(c); - - r = pa_signal_init(pa_mainloop_get_api(mainloop)); - assert(r == 0); - pa_signal_new(SIGINT, signal_callback, c); - pa_signal_new(SIGTERM, signal_callback, c); -#ifdef SIGPIPE - signal(SIGPIPE, SIG_IGN); -#endif - -#ifdef OS_IS_WIN32 - defer = pa_mainloop_get_api(mainloop)->defer_new(pa_mainloop_get_api(mainloop), message_cb, NULL); - assert(defer); -#endif - - if (conf->daemonize) - c->running_as_daemon = 1; - -#ifdef SIGUSR1 - pa_signal_new(SIGUSR1, signal_callback, c); -#endif -#ifdef SIGUSR2 - pa_signal_new(SIGUSR2, signal_callback, c); -#endif -#ifdef SIGHUP - pa_signal_new(SIGHUP, signal_callback, c); -#endif - - oil_init(); - - r = pa_cpu_limit_init(pa_mainloop_get_api(mainloop)); - assert(r == 0); - - buf = pa_strbuf_new(); - assert(buf); - if (conf->default_script_file) - r = pa_cli_command_execute_file(c, conf->default_script_file, buf, &conf->fail); - - if (r >= 0) - r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail); - pa_log(s = pa_strbuf_tostring_free(buf)); - pa_xfree(s); - - if (r < 0 && conf->fail) { - pa_log(__FILE__": failed to initialize daemon.\n"); -#ifdef HAVE_FORK - if (conf->daemonize) - pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); -#endif - } else if (!c->modules || pa_idxset_size(c->modules) == 0) { - pa_log(__FILE__": daemon startup without any loaded modules, refusing to work.\n"); -#ifdef HAVE_FORK - if (conf->daemonize) - pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); -#endif - } else { - - retval = 0; -#ifdef HAVE_FORK - if (conf->daemonize) - pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); -#endif - - c->disallow_module_loading = conf->disallow_module_loading; - c->exit_idle_time = conf->exit_idle_time; - c->module_idle_time = conf->module_idle_time; - c->scache_idle_time = conf->scache_idle_time; - c->resample_method = conf->resample_method; - - if (c->default_sink_name && - pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, 1) == NULL) { - pa_log_error("%s : Fatal error. Default sink name (%s) does not exist in name register.\n", __FILE__, c->default_sink_name); - retval = 1; - } else { - pa_log_info(__FILE__": Daemon startup complete.\n"); - if (pa_mainloop_run(mainloop, &retval) < 0) - retval = 1; - pa_log_info(__FILE__": Daemon shutdown initiated.\n"); - } - } - -#ifdef OS_IS_WIN32 - pa_mainloop_get_api(mainloop)->defer_free(defer); -#endif - - pa_core_free(c); - - pa_cpu_limit_done(); - pa_signal_done(); - pa_mainloop_free(mainloop); - - pa_log_info(__FILE__": Daemon terminated.\n"); - -finish: - - if (conf) - pa_daemon_conf_free(conf); - - if (valid_pid_file) - pa_pid_file_remove(); - - close_pipe(daemon_pipe); - -#ifdef OS_IS_WIN32 - WSACleanup(); -#endif - - lt_dlexit(); - - return retval; -} diff --git a/polyp/mainloop-api.c b/polyp/mainloop-api.c deleted file mode 100644 index 3229ec20..00000000 --- a/polyp/mainloop-api.c +++ /dev/null @@ -1,68 +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 -#endif - -#include -#include - -#include "mainloop-api.h" -#include "xmalloc.h" -#include "gccmacro.h" - -struct once_info { - void (*callback)(pa_mainloop_api*m, void *userdata); - void *userdata; -}; - -static void once_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { - struct once_info *i = userdata; - assert(m && i && i->callback); - - i->callback(m, i->userdata); - - assert(m->defer_free); - m->defer_free(e); -} - -static void free_callback(pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event *e, void *userdata) { - struct once_info *i = userdata; - assert(m && i); - pa_xfree(i); -} - -void pa_mainloop_api_once(pa_mainloop_api* m, void (*callback)(pa_mainloop_api *m, void *userdata), void *userdata) { - struct once_info *i; - pa_defer_event *e; - assert(m && callback); - - i = pa_xnew(struct once_info, 1); - i->callback = callback; - i->userdata = userdata; - - assert(m->defer_new); - e = m->defer_new(m, once_callback, i); - assert(e); - m->defer_set_destroy(e, free_callback); -} - diff --git a/polyp/mainloop-api.h b/polyp/mainloop-api.h deleted file mode 100644 index 91ee4111..00000000 --- a/polyp/mainloop-api.h +++ /dev/null @@ -1,120 +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 -#include - -#include - -/** \file - * - * Main loop abstraction layer. Both the polypaudio core and the - * polypaudio client library use a main loop abstraction layer. Due to - * this it is possible to embed polypaudio into other - * applications easily. Two main loop implemenations are - * currently available: - * \li A minimal implementation based on the C library's poll() function (See \ref mainloop.h) - * \li A wrapper around the GLIB main loop. Use this to embed polypaudio into your GLIB/GTK+/GNOME programs (See \ref glib-mainloop.h) - * - * The structure pa_mainloop_api is used as vtable for the main loop abstraction. - * - * This mainloop abstraction layer has no direct support for UNIX signals. Generic, mainloop implementation agnostic support is available throught \ref mainloop-signal.h. - * */ - -PA_C_DECL_BEGIN - -/** A bitmask for IO events */ -typedef enum pa_io_event_flags { - PA_IO_EVENT_NULL = 0, /**< No event */ - PA_IO_EVENT_INPUT = 1, /**< Input event */ - PA_IO_EVENT_OUTPUT = 2, /**< Output event */ - PA_IO_EVENT_HANGUP = 4, /**< Hangup event */ - PA_IO_EVENT_ERROR = 8 /**< Error event */ -} pa_io_event_flags_t; - -/** \pa_io_event - * An opaque IO event source object */ -typedef struct pa_io_event pa_io_event; - -/** \pa_defer_event - * An opaque deferred event source object. Events of this type are triggered once in every main loop iteration */ -typedef struct pa_defer_event pa_defer_event; - -/** \pa_time_event - * An opaque timer event source object */ -typedef struct pa_time_event pa_time_event; - -/** An abstract mainloop API vtable */ -typedef struct pa_mainloop_api pa_mainloop_api; - -struct pa_mainloop_api { - /** A pointer to some private, arbitrary data of the main loop implementation */ - void *userdata; - - /** Create a new IO event source object */ - pa_io_event* (*io_new)(pa_mainloop_api*a, int fd, pa_io_event_flags_t events, void (*callback) (pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata), void *userdata); - - /** Enable or disable IO events on this object */ - void (*io_enable)(pa_io_event* e, pa_io_event_flags_t events); - - /** Free a IO event source object */ - void (*io_free)(pa_io_event* e); - - /** Set a function that is called when the IO event source is destroyed. Use this to free the userdata argument if required */ - void (*io_set_destroy)(pa_io_event *e, void (*callback) (pa_mainloop_api*a, pa_io_event *e, void *userdata)); - - /** Create a new timer event source object for the specified Unix time */ - pa_time_event* (*time_new)(pa_mainloop_api*a, const struct timeval *tv, void (*callback) (pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata), void *userdata); - - /** Restart a running or expired timer event source with a new Unix time */ - void (*time_restart)(pa_time_event* e, const struct timeval *tv); - - /** Free a deferred timer event source object */ - void (*time_free)(pa_time_event* e); - - /** Set a function that is called when the timer event source is destroyed. Use this to free the userdata argument if required */ - void (*time_set_destroy)(pa_time_event *e, void (*callback) (pa_mainloop_api*a, pa_time_event *e, void *userdata)); - - /** Create a new deferred event source object */ - pa_defer_event* (*defer_new)(pa_mainloop_api*a, void (*callback) (pa_mainloop_api*a, pa_defer_event* e, void *userdata), void *userdata); - - /** Enable or disable a deferred event source temporarily */ - void (*defer_enable)(pa_defer_event* e, int b); - - /** Free a deferred event source object */ - void (*defer_free)(pa_defer_event* e); - - /** Set a function that is called when the deferred event source is destroyed. Use this to free the userdata argument if required */ - void (*defer_set_destroy)(pa_defer_event *e, void (*callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata)); - - /** Exit the main loop and return the specfied retval*/ - void (*quit)(pa_mainloop_api*a, int retval); -}; - -/** Run the specified callback function once from the main loop using an anonymous defer event. */ -void pa_mainloop_api_once(pa_mainloop_api*m, void (*callback)(pa_mainloop_api*m, void *userdata), void *userdata); - -PA_C_DECL_END - -#endif diff --git a/polyp/mainloop-signal.c b/polyp/mainloop-signal.c deleted file mode 100644 index 76936031..00000000 --- a/polyp/mainloop-signal.c +++ /dev/null @@ -1,267 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_WINDOWS_H -#include -#endif - -#include "mainloop-signal.h" -#include "util.h" -#include "xmalloc.h" -#include "log.h" -#include "gccmacro.h" - -struct pa_signal_event { - int sig; -#ifdef HAVE_SIGACTION - struct sigaction saved_sigaction; -#else - void (*saved_handler)(int sig); -#endif - void (*callback) (pa_mainloop_api*a, pa_signal_event *e, int sig, void *userdata); - void *userdata; - void (*destroy_callback) (pa_mainloop_api*a, pa_signal_event*e, void *userdata); - pa_signal_event *previous, *next; -}; - -static pa_mainloop_api *api = NULL; -static int signal_pipe[2] = { -1, -1 }; -static pa_io_event* io_event = NULL; -static pa_defer_event *defer_event = NULL; -static pa_signal_event *signals = NULL; - -#ifdef OS_IS_WIN32 -static unsigned int waiting_signals = 0; -static CRITICAL_SECTION crit; -#endif - -static void signal_handler(int sig) { -#ifndef HAVE_SIGACTION - signal(sig, signal_handler); -#endif - write(signal_pipe[1], &sig, sizeof(sig)); - -#ifdef OS_IS_WIN32 - EnterCriticalSection(&crit); - waiting_signals++; - LeaveCriticalSection(&crit); -#endif -} - -static void dispatch(pa_mainloop_api*a, int sig) { - pa_signal_event*s; - - for (s = signals; s; s = s->next) - if (s->sig == sig) { - assert(s->callback); - s->callback(a, s, sig, s->userdata); - break; - } -} - -static void defer(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event*e, PA_GCC_UNUSED void *userdata) { - ssize_t r; - int sig; - unsigned int sigs; - -#ifdef OS_IS_WIN32 - EnterCriticalSection(&crit); - sigs = waiting_signals; - waiting_signals = 0; - LeaveCriticalSection(&crit); -#endif - - while (sigs) { - if ((r = read(signal_pipe[0], &sig, sizeof(sig))) < 0) { - pa_log(__FILE__": read(): %s\n", strerror(errno)); - return; - } - - if (r != sizeof(sig)) { - pa_log(__FILE__": short read()\n"); - return; - } - - dispatch(a, sig); - - sigs--; - } -} - -static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags_t f, PA_GCC_UNUSED void *userdata) { - ssize_t r; - int sig; - assert(a && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == signal_pipe[0]); - - - if ((r = read(signal_pipe[0], &sig, sizeof(sig))) < 0) { - if (errno == EAGAIN) - return; - - pa_log(__FILE__": read(): %s\n", strerror(errno)); - return; - } - - if (r != sizeof(sig)) { - pa_log(__FILE__": short read()\n"); - return; - } - - dispatch(a, sig); -} - -int pa_signal_init(pa_mainloop_api *a) { - assert(!api && a && signal_pipe[0] == -1 && signal_pipe[1] == -1 && !io_event && !defer_event); - -#ifdef OS_IS_WIN32 - if (_pipe(signal_pipe, 200, _O_BINARY) < 0) { -#else - if (pipe(signal_pipe) < 0) { -#endif - pa_log(__FILE__": pipe() failed: %s\n", strerror(errno)); - return -1; - } - - pa_make_nonblock_fd(signal_pipe[0]); - pa_make_nonblock_fd(signal_pipe[1]); - pa_fd_set_cloexec(signal_pipe[0], 1); - pa_fd_set_cloexec(signal_pipe[1], 1); - - api = a; - -#ifndef OS_IS_WIN32 - io_event = api->io_new(api, signal_pipe[0], PA_IO_EVENT_INPUT, callback, NULL); - assert(io_event); -#else - defer_event = api->defer_new(api, defer, NULL); - assert(defer_event); - - InitializeCriticalSection(&crit); -#endif - - return 0; -} - -void pa_signal_done(void) { - assert(api && signal_pipe[0] >= 0 && signal_pipe[1] >= 0 && (io_event || defer_event)); - - while (signals) - pa_signal_free(signals); - - -#ifndef OS_IS_WIN32 - api->io_free(io_event); - io_event = NULL; -#else - api->defer_free(defer_event); - defer_event = NULL; - - DeleteCriticalSection(&crit); -#endif - - close(signal_pipe[0]); - close(signal_pipe[1]); - signal_pipe[0] = signal_pipe[1] = -1; - - api = NULL; -} - -pa_signal_event* pa_signal_new(int sig, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata) { - pa_signal_event *e = NULL; - -#ifdef HAVE_SIGACTION - struct sigaction sa; -#endif - - assert(sig > 0 && _callback); - - for (e = signals; e; e = e->next) - if (e->sig == sig) - goto fail; - - e = pa_xmalloc(sizeof(pa_signal_event)); - e->sig = sig; - e->callback = _callback; - e->userdata = userdata; - e->destroy_callback = NULL; - -#ifdef HAVE_SIGACTION - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = signal_handler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - - if (sigaction(sig, &sa, &e->saved_sigaction) < 0) -#else - if ((e->saved_handler = signal(sig, signal_handler)) == SIG_ERR) -#endif - goto fail; - - e->previous = NULL; - e->next = signals; - signals = e; - - return e; -fail: - if (e) - pa_xfree(e); - return NULL; -} - -void pa_signal_free(pa_signal_event *e) { - assert(e); - - if (e->next) - e->next->previous = e->previous; - if (e->previous) - e->previous->next = e->next; - else - signals = e->next; - -#ifdef HAVE_SIGACTION - sigaction(e->sig, &e->saved_sigaction, NULL); -#else - signal(e->sig, e->saved_handler); -#endif - - if (e->destroy_callback) - e->destroy_callback(api, e, e->userdata); - - pa_xfree(e); -} - -void pa_signal_set_destroy(pa_signal_event *e, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata)) { - assert(e); - e->destroy_callback = _callback; -} diff --git a/polyp/mainloop-signal.h b/polyp/mainloop-signal.h deleted file mode 100644 index 6ce31370..00000000 --- a/polyp/mainloop-signal.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef foomainloopsignalhfoo -#define foomainloopsignalhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "mainloop-api.h" -#include "cdecl.h" - -PA_C_DECL_BEGIN - -/** \file - * UNIX signal support for main loops. In contrast to other - * main loop event sources such as timer and IO events, UNIX signal - * support requires modification of the global process - * environment. Due to this the generic main loop abstraction layer as - * defined in \ref mainloop-api.h doesn't have direct support for UNIX - * signals. However, you may hook signal support into an abstract main loop via the routines defined herein. - */ - -/** Initialize the UNIX signal subsystem and bind it to the specified main loop */ -int pa_signal_init(pa_mainloop_api *api); - -/** Cleanup the signal subsystem */ -void pa_signal_done(void); - -/** \pa_signal_event - * An opaque UNIX signal event source object */ -typedef struct pa_signal_event pa_signal_event; - -/** Create a new UNIX signal event source object */ -pa_signal_event* pa_signal_new(int sig, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata); - -/** Free a UNIX signal event source object */ -void pa_signal_free(pa_signal_event *e); - -/** Set a function that is called when the signal event source is destroyed. Use this to free the userdata argument if required */ -void pa_signal_set_destroy(pa_signal_event *e, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata)); - -PA_C_DECL_END - -#endif diff --git a/polyp/mainloop-test.c b/polyp/mainloop-test.c deleted file mode 100644 index ee0f8711..00000000 --- a/polyp/mainloop-test.c +++ /dev/null @@ -1,146 +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 -#endif - -#include -#include -#include -#include - -#include "util.h" -#include "gccmacro.h" - -#ifdef GLIB_MAIN_LOOP - -#include -#include "glib-mainloop.h" - -static GMainLoop* glib_main_loop = NULL; - -#if GLIB_MAJOR_VERSION >= 2 -#define GLIB20 -#else -#undef GLIB20 -#endif - - -#else /* GLIB_MAIN_LOOP */ -#include "mainloop.h" -#endif /* GLIB_MAIN_LOOP */ - -static pa_defer_event *de; - -static void iocb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) { - unsigned char c; - read(fd, &c, sizeof(c)); - fprintf(stderr, "IO EVENT: %c\n", c < 32 ? '.' : c); - a->defer_enable(de, 1); -} - -static void dcb(pa_mainloop_api*a, pa_defer_event *e, void *userdata) { - fprintf(stderr, "DEFER EVENT\n"); - a->defer_enable(e, 0); -} - -static void tcb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) { - fprintf(stderr, "TIME EVENT\n"); - -#if defined(GLIB_MAIN_LOOP) && defined(GLIB20) - g_main_loop_quit(glib_main_loop); -#elif defined(GLIB_MAIN_LOOP) - g_main_quit(glib_main_loop); -#else - a->quit(a, 0); -#endif -} - -int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { - pa_mainloop_api *a; - pa_io_event *ioe; - pa_time_event *te; - struct timeval tv; - -#ifdef GLIB_MAIN_LOOP - pa_glib_mainloop *g; - -#ifdef GLIB20 - glib_main_loop = g_main_loop_new(NULL, FALSE); - assert(glib_main_loop); - - g = pa_glib_mainloop_new(NULL); -#else /* GLIB20 */ - glib_main_loop = g_main_new(FALSE); - assert(glib_main_loop); - - g = pa_glib_mainloop_new(); -#endif /* GLIB20 */ - assert(g); - - a = pa_glib_mainloop_get_api(g); - assert(a); -#else /* GLIB_MAIN_LOOP */ - pa_mainloop *m; - - m = pa_mainloop_new(); - assert(m); - - a = pa_mainloop_get_api(m); - assert(a); -#endif /* GLIB_MAIN_LOOP */ - - ioe = a->io_new(a, 0, PA_IO_EVENT_INPUT, iocb, NULL); - assert(ioe); - - de = a->defer_new(a, dcb, NULL); - assert(de); - - pa_gettimeofday(&tv); - tv.tv_sec += 10; - te = a->time_new(a, &tv, tcb, NULL); - -#if defined(GLIB_MAIN_LOOP) && defined(GLIB20) - g_main_loop_run(glib_main_loop); -#elif defined(GLIB_MAIN_LOOP) - g_main_run(glib_main_loop); -#else - pa_mainloop_run(m, NULL); -#endif - - a->time_free(te); - a->defer_free(de); - a->io_free(ioe); - -#ifdef GLIB_MAIN_LOOP - pa_glib_mainloop_free(g); -#ifdef GLIB20 - g_main_loop_unref(glib_main_loop); -#else - g_main_destroy(glib_main_loop); -#endif -#else - pa_mainloop_free(m); -#endif - - return 0; -} diff --git a/polyp/mainloop.c b/polyp/mainloop.c deleted file mode 100644 index d25af78a..00000000 --- a/polyp/mainloop.c +++ /dev/null @@ -1,812 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SYS_POLL_H -#include -#else -#include "poll.h" -#endif - -#include "winsock.h" - -#include "mainloop.h" -#include "util.h" -#include "idxset.h" -#include "xmalloc.h" -#include "log.h" - -struct pa_io_event { - pa_mainloop *mainloop; - int dead; - int fd; - pa_io_event_flags_t events; - void (*callback) (pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata); - struct pollfd *pollfd; - void *userdata; - void (*destroy_callback) (pa_mainloop_api*a, pa_io_event *e, void *userdata); -}; - -struct pa_time_event { - pa_mainloop *mainloop; - int dead; - int enabled; - struct timeval timeval; - void (*callback)(pa_mainloop_api*a, pa_time_event *e, const struct timeval*tv, void *userdata); - void *userdata; - void (*destroy_callback) (pa_mainloop_api*a, pa_time_event *e, void *userdata); -}; - -struct pa_defer_event { - pa_mainloop *mainloop; - int dead; - int enabled; - void (*callback)(pa_mainloop_api*a, pa_defer_event*e, void *userdata); - void *userdata; - void (*destroy_callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata); -}; - -struct pa_mainloop { - pa_idxset *io_events, *time_events, *defer_events; - int io_events_scan_dead, defer_events_scan_dead, time_events_scan_dead; - - struct pollfd *pollfds; - unsigned max_pollfds, n_pollfds; - int rebuild_pollfds; - - int prepared_timeout; - - int quit, retval; - pa_mainloop_api api; - - int deferred_pending; - - int wakeup_pipe[2]; - - enum { - STATE_PASSIVE, - STATE_PREPARED, - STATE_POLLING, - STATE_POLLED, - STATE_QUIT - } state; -}; - -/* IO events */ -static pa_io_event* mainloop_io_new( - pa_mainloop_api*a, - int fd, - pa_io_event_flags_t events, - void (*callback) (pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata), - void *userdata) { - - pa_mainloop *m; - pa_io_event *e; - - assert(a && a->userdata && fd >= 0 && callback); - m = a->userdata; - assert(a == &m->api); - - pa_mainloop_wakeup(m); - - e = pa_xmalloc(sizeof(pa_io_event)); - e->mainloop = m; - e->dead = 0; - - e->fd = fd; - e->events = events; - e->callback = callback; - e->userdata = userdata; - e->destroy_callback = NULL; - e->pollfd = NULL; - -#ifdef OS_IS_WIN32 - { - fd_set xset; - struct timeval tv; - - tv.tv_sec = 0; - tv.tv_usec = 0; - - FD_ZERO (&xset); - FD_SET (fd, &xset); - - if ((select((SELECT_TYPE_ARG1) fd, NULL, NULL, SELECT_TYPE_ARG234 &xset, - SELECT_TYPE_ARG5 &tv) == -1) && - (WSAGetLastError() == WSAENOTSOCK)) { - pa_log_warn(__FILE__": WARNING: cannot monitor non-socket file descriptors.\n"); - e->dead = 1; - } - } -#endif - - pa_idxset_put(m->io_events, e, NULL); - m->rebuild_pollfds = 1; - return e; -} - -static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) { - assert(e && e->mainloop); - - pa_mainloop_wakeup(e->mainloop); - - e->events = events; - e->mainloop->rebuild_pollfds = 1; -} - -static void mainloop_io_free(pa_io_event *e) { - assert(e && e->mainloop); - - pa_mainloop_wakeup(e->mainloop); - - e->dead = e->mainloop->io_events_scan_dead = e->mainloop->rebuild_pollfds = 1; -} - -static void mainloop_io_set_destroy(pa_io_event *e, void (*callback)(pa_mainloop_api*a, pa_io_event *e, void *userdata)) { - assert(e); - e->destroy_callback = callback; -} - -/* Defer events */ -static pa_defer_event* mainloop_defer_new(pa_mainloop_api*a, void (*callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata), void *userdata) { - pa_mainloop *m; - pa_defer_event *e; - - assert(a && a->userdata && callback); - m = a->userdata; - assert(a == &m->api); - - e = pa_xmalloc(sizeof(pa_defer_event)); - e->mainloop = m; - e->dead = 0; - - e->enabled = 1; - e->callback = callback; - e->userdata = userdata; - e->destroy_callback = NULL; - - pa_idxset_put(m->defer_events, e, NULL); - - m->deferred_pending++; - return e; -} - -static void mainloop_defer_enable(pa_defer_event *e, int b) { - assert(e); - - if (e->enabled && !b) { - assert(e->mainloop->deferred_pending > 0); - e->mainloop->deferred_pending--; - } else if (!e->enabled && b) - e->mainloop->deferred_pending++; - - e->enabled = b; -} - -static void mainloop_defer_free(pa_defer_event *e) { - assert(e); - e->dead = e->mainloop->defer_events_scan_dead = 1; - - if (e->enabled) { - e->enabled = 0; - assert(e->mainloop->deferred_pending > 0); - e->mainloop->deferred_pending--; - } -} - -static void mainloop_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api*a, pa_defer_event *e, void *userdata)) { - assert(e); - e->destroy_callback = callback; -} - -/* Time events */ -static pa_time_event* mainloop_time_new(pa_mainloop_api*a, const struct timeval *tv, void (*callback) (pa_mainloop_api*a, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) { - pa_mainloop *m; - pa_time_event *e; - - assert(a && a->userdata && callback); - m = a->userdata; - assert(a == &m->api); - - pa_mainloop_wakeup(m); - - e = pa_xmalloc(sizeof(pa_time_event)); - e->mainloop = m; - e->dead = 0; - - e->enabled = !!tv; - if (tv) - e->timeval = *tv; - - e->callback = callback; - e->userdata = userdata; - e->destroy_callback = NULL; - - pa_idxset_put(m->time_events, e, NULL); - - return e; -} - -static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) { - assert(e); - - pa_mainloop_wakeup(e->mainloop); - - if (tv) { - e->enabled = 1; - e->timeval = *tv; - } else - e->enabled = 0; -} - -static void mainloop_time_free(pa_time_event *e) { - assert(e); - - pa_mainloop_wakeup(e->mainloop); - - e->dead = e->mainloop->time_events_scan_dead = 1; -} - -static void mainloop_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*a, pa_time_event *e, void *userdata)) { - assert(e); - e->destroy_callback = callback; -} - -/* quit() */ - -static void mainloop_quit(pa_mainloop_api*a, int retval) { - pa_mainloop *m; - assert(a && a->userdata); - m = a->userdata; - assert(a == &m->api); - - pa_mainloop_wakeup(m); - - m->quit = 1; - m->retval = retval; -} - -static const pa_mainloop_api vtable = { - .userdata = NULL, - - .io_new= mainloop_io_new, - .io_enable= mainloop_io_enable, - .io_free= mainloop_io_free, - .io_set_destroy= mainloop_io_set_destroy, - - .time_new = mainloop_time_new, - .time_restart = mainloop_time_restart, - .time_free = mainloop_time_free, - .time_set_destroy = mainloop_time_set_destroy, - - .defer_new = mainloop_defer_new, - .defer_enable = mainloop_defer_enable, - .defer_free = mainloop_defer_free, - .defer_set_destroy = mainloop_defer_set_destroy, - - .quit = mainloop_quit, -}; - -pa_mainloop *pa_mainloop_new(void) { - pa_mainloop *m; - - m = pa_xmalloc(sizeof(pa_mainloop)); - -#ifndef OS_ISWIN32 - if (pipe(m->wakeup_pipe) < 0) { - pa_xfree(m); - return NULL; - } - - pa_make_nonblock_fd(m->wakeup_pipe[0]); - pa_make_nonblock_fd(m->wakeup_pipe[1]); -#else - m->wakeup_pipe[0] = -1; - m->wakeup_pipe[1] = -1; -#endif - - m->io_events = pa_idxset_new(NULL, NULL); - m->defer_events = pa_idxset_new(NULL, NULL); - m->time_events = pa_idxset_new(NULL, NULL); - - assert(m->io_events && m->defer_events && m->time_events); - - m->io_events_scan_dead = m->defer_events_scan_dead = m->time_events_scan_dead = 0; - - m->pollfds = NULL; - m->max_pollfds = m->n_pollfds = m->rebuild_pollfds = 0; - - m->quit = m->retval = 0; - - m->api = vtable; - m->api.userdata = m; - - m->deferred_pending = 0; - - m->state = STATE_PASSIVE; - - return m; -} - -static int io_foreach(void *p, uint32_t PA_GCC_UNUSED idx, int *del, void*userdata) { - pa_io_event *e = p; - int *all = userdata; - assert(e && del && all); - - if (!*all && !e->dead) - return 0; - - if (e->destroy_callback) - e->destroy_callback(&e->mainloop->api, e, e->userdata); - pa_xfree(e); - *del = 1; - return 0; -} - -static int time_foreach(void *p, uint32_t PA_GCC_UNUSED idx, int *del, void*userdata) { - pa_time_event *e = p; - int *all = userdata; - assert(e && del && all); - - if (!*all && !e->dead) - return 0; - - if (e->destroy_callback) - e->destroy_callback(&e->mainloop->api, e, e->userdata); - pa_xfree(e); - *del = 1; - return 0; -} - -static int defer_foreach(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void*userdata) { - pa_defer_event *e = p; - int *all = userdata; - assert(e && del && all); - - if (!*all && !e->dead) - return 0; - - if (e->destroy_callback) - e->destroy_callback(&e->mainloop->api, e, e->userdata); - pa_xfree(e); - *del = 1; - return 0; -} - -void pa_mainloop_free(pa_mainloop* m) { - int all = 1; - assert(m && (m->state != STATE_POLLING)); - - pa_idxset_foreach(m->io_events, io_foreach, &all); - pa_idxset_foreach(m->time_events, time_foreach, &all); - pa_idxset_foreach(m->defer_events, defer_foreach, &all); - - pa_idxset_free(m->io_events, NULL, NULL); - pa_idxset_free(m->time_events, NULL, NULL); - pa_idxset_free(m->defer_events, NULL, NULL); - - pa_xfree(m->pollfds); - - if (m->wakeup_pipe[0] >= 0) - close(m->wakeup_pipe[0]); - if (m->wakeup_pipe[1] >= 0) - close(m->wakeup_pipe[1]); - - pa_xfree(m); -} - -static void scan_dead(pa_mainloop *m) { - int all = 0; - assert(m); - - if (m->io_events_scan_dead) - pa_idxset_foreach(m->io_events, io_foreach, &all); - if (m->time_events_scan_dead) - pa_idxset_foreach(m->time_events, time_foreach, &all); - if (m->defer_events_scan_dead) - pa_idxset_foreach(m->defer_events, defer_foreach, &all); - - m->io_events_scan_dead = m->time_events_scan_dead = m->defer_events_scan_dead = 0; -} - -static void rebuild_pollfds(pa_mainloop *m) { - pa_io_event*e; - struct pollfd *p; - uint32_t idx = PA_IDXSET_INVALID; - unsigned l; - - l = pa_idxset_size(m->io_events) + 1; - if (m->max_pollfds < l) { - m->pollfds = pa_xrealloc(m->pollfds, sizeof(struct pollfd)*l); - m->max_pollfds = l; - } - - m->n_pollfds = 0; - p = m->pollfds; - - if (m->wakeup_pipe[0] >= 0) { - m->pollfds[0].fd = m->wakeup_pipe[0]; - m->pollfds[0].events = POLLIN; - m->pollfds[0].revents = 0; - p++; - m->n_pollfds++; - } - - for (e = pa_idxset_first(m->io_events, &idx); e; e = pa_idxset_next(m->io_events, &idx)) { - if (e->dead) { - e->pollfd = NULL; - continue; - } - - e->pollfd = p; - p->fd = e->fd; - p->events = - ((e->events & PA_IO_EVENT_INPUT) ? POLLIN : 0) | - ((e->events & PA_IO_EVENT_OUTPUT) ? POLLOUT : 0) | - POLLHUP | - POLLERR; - p->revents = 0; - - p++; - m->n_pollfds++; - } - - m->rebuild_pollfds = 0; -} - -static int dispatch_pollfds(pa_mainloop *m) { - uint32_t idx = PA_IDXSET_INVALID; - pa_io_event *e; - int r = 0; - - for (e = pa_idxset_first(m->io_events, &idx); e && !m->quit; e = pa_idxset_next(m->io_events, &idx)) { - if (e->dead || !e->pollfd || !e->pollfd->revents) - continue; - - assert(e->pollfd->fd == e->fd && e->callback); - e->callback(&m->api, e, e->fd, - (e->pollfd->revents & POLLHUP ? PA_IO_EVENT_HANGUP : 0) | - (e->pollfd->revents & POLLIN ? PA_IO_EVENT_INPUT : 0) | - (e->pollfd->revents & POLLOUT ? PA_IO_EVENT_OUTPUT : 0) | - (e->pollfd->revents & POLLERR ? PA_IO_EVENT_ERROR : 0), - e->userdata); - e->pollfd->revents = 0; - r++; - } - - return r; -} - -static int dispatch_defer(pa_mainloop *m) { - uint32_t idx; - pa_defer_event *e; - int r = 0; - - if (!m->deferred_pending) - return 0; - - for (e = pa_idxset_first(m->defer_events, &idx); e && !m->quit; e = pa_idxset_next(m->defer_events, &idx)) { - if (e->dead || !e->enabled) - continue; - - assert(e->callback); - e->callback(&m->api, e, e->userdata); - r++; - } - - return r; -} - -static int calc_next_timeout(pa_mainloop *m) { - uint32_t idx; - pa_time_event *e; - struct timeval now; - int t = -1; - int got_time = 0; - - if (pa_idxset_isempty(m->time_events)) - return -1; - - for (e = pa_idxset_first(m->time_events, &idx); e; e = pa_idxset_next(m->time_events, &idx)) { - int tmp; - - if (e->dead || !e->enabled) - continue; - - /* Let's save a system call */ - if (!got_time) { - pa_gettimeofday(&now); - got_time = 1; - } - - if (e->timeval.tv_sec < now.tv_sec || (e->timeval.tv_sec == now.tv_sec && e->timeval.tv_usec <= now.tv_usec)) - return 0; - - tmp = (e->timeval.tv_sec - now.tv_sec)*1000; - - if (e->timeval.tv_usec > now.tv_usec) - tmp += (e->timeval.tv_usec - now.tv_usec)/1000; - else - tmp -= (now.tv_usec - e->timeval.tv_usec)/1000; - - if (tmp == 0) - return 0; - else if (t == -1 || tmp < t) - t = tmp; - } - - return t; -} - -static int dispatch_timeout(pa_mainloop *m) { - uint32_t idx; - pa_time_event *e; - struct timeval now; - int got_time = 0; - int r = 0; - assert(m); - - if (pa_idxset_isempty(m->time_events)) - return 0; - - for (e = pa_idxset_first(m->time_events, &idx); e && !m->quit; e = pa_idxset_next(m->time_events, &idx)) { - - if (e->dead || !e->enabled) - continue; - - /* Let's save a system call */ - if (!got_time) { - pa_gettimeofday(&now); - got_time = 1; - } - - if (e->timeval.tv_sec < now.tv_sec || (e->timeval.tv_sec == now.tv_sec && e->timeval.tv_usec <= now.tv_usec)) { - assert(e->callback); - - e->enabled = 0; - e->callback(&m->api, e, &e->timeval, e->userdata); - - r++; - } - } - - return r; -} - -void pa_mainloop_wakeup(pa_mainloop *m) { - char c = 'W'; - assert(m); - - if (m->wakeup_pipe[1] >= 0) - write(m->wakeup_pipe[1], &c, sizeof(c)); -} - -static void clear_wakeup(pa_mainloop *m) { - char c[10]; - - assert(m); - - if (m->wakeup_pipe[0] < 0) - return; - - while (read(m->wakeup_pipe[0], &c, sizeof(c)) == sizeof(c)); -} - -int pa_mainloop_prepare(pa_mainloop *m, int timeout) { - int dispatched = 0; - - assert(m && (m->state == STATE_PASSIVE)); - - clear_wakeup(m); - - scan_dead(m); - - if (m->quit) - goto quit; - - dispatched += dispatch_defer(m); - - if (m->quit) - goto quit; - - if (m->rebuild_pollfds) - rebuild_pollfds(m); - - m->prepared_timeout = calc_next_timeout(m); - if ((timeout >= 0) && (m->prepared_timeout > timeout)) - m->prepared_timeout = timeout; - - m->state = STATE_PREPARED; - - return dispatched; - -quit: - - m->state = STATE_QUIT; - - return -2; -} - -int pa_mainloop_poll(pa_mainloop *m) { - int r; - - assert(m && (m->state == STATE_PREPARED)); - - m->state = STATE_POLLING; - - r = poll(m->pollfds, m->n_pollfds, m->prepared_timeout); - - if ((r < 0) && (errno == EINTR)) - r = 0; - - if (r < 0) - m->state = STATE_PASSIVE; - else - m->state = STATE_POLLED; - - return r; -} - -int pa_mainloop_dispatch(pa_mainloop *m) { - int dispatched = 0; - - assert(m && (m->state == STATE_POLLED)); - - dispatched += dispatch_timeout(m); - - if (m->quit) - goto quit; - - dispatched += dispatch_pollfds(m); - - if (m->quit) - goto quit; - - m->state = STATE_PASSIVE; - - return dispatched; - -quit: - - m->state = STATE_QUIT; - - return -2; -} - -int pa_mainloop_get_retval(pa_mainloop *m) { - assert(m); - return m->retval; -} - -int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) { - int r, dispatched = 0; - - assert(m); - - r = pa_mainloop_prepare(m, block ? -1 : 0); - if (r < 0) { - if ((r == -2) && retval) - *retval = pa_mainloop_get_retval(m); - return r; - } - - dispatched += r; - - r = pa_mainloop_poll(m); - if (r < 0) { - pa_log(__FILE__": poll(): %s\n", strerror(errno)); - return r; - } - - r = pa_mainloop_dispatch(m); - if (r < 0) { - if ((r == -2) && retval) - *retval = pa_mainloop_get_retval(m); - return r; - } - - dispatched += r; - - return dispatched; -} - -int pa_mainloop_run(pa_mainloop *m, int *retval) { - int r; - while ((r = pa_mainloop_iterate(m, 1, retval)) >= 0); - - if (r == -2) - return 1; - else if (r < 0) - return -1; - else - return 0; -} - -void pa_mainloop_quit(pa_mainloop *m, int r) { - assert(m); - pa_mainloop_wakeup(m); - m->quit = r; -} - -pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m) { - assert(m); - return &m->api; -} - -int pa_mainloop_deferred_pending(pa_mainloop *m) { - assert(m); - return m->deferred_pending > 0; -} - - -#if 0 -void pa_mainloop_dump(pa_mainloop *m) { - assert(m); - - pa_log(__FILE__": Dumping mainloop sources START\n"); - - { - uint32_t idx = PA_IDXSET_INVALID; - pa_io_event *e; - for (e = pa_idxset_first(m->io_events, &idx); e; e = pa_idxset_next(m->io_events, &idx)) { - if (e->dead) - continue; - - pa_log(__FILE__": kind=io fd=%i events=%i callback=%p userdata=%p\n", e->fd, (int) e->events, (void*) e->callback, (void*) e->userdata); - } - } - { - uint32_t idx = PA_IDXSET_INVALID; - pa_defer_event *e; - for (e = pa_idxset_first(m->defer_events, &idx); e; e = pa_idxset_next(m->defer_events, &idx)) { - if (e->dead) - continue; - - pa_log(__FILE__": kind=defer enabled=%i callback=%p userdata=%p\n", e->enabled, (void*) e->callback, (void*) e->userdata); - } - } - { - uint32_t idx = PA_IDXSET_INVALID; - pa_time_event *e; - for (e = pa_idxset_first(m->time_events, &idx); e; e = pa_idxset_next(m->time_events, &idx)) { - if (e->dead) - continue; - - pa_log(__FILE__": kind=time enabled=%i time=%lu.%lu callback=%p userdata=%p\n", e->enabled, (unsigned long) e->timeval.tv_sec, (unsigned long) e->timeval.tv_usec, (void*) e->callback, (void*) e->userdata); - } - } - - pa_log(__FILE__": Dumping mainloop sources STOP\n"); - -} -#endif diff --git a/polyp/mainloop.h b/polyp/mainloop.h deleted file mode 100644 index 921a0709..00000000 --- a/polyp/mainloop.h +++ /dev/null @@ -1,90 +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.*/ - -/** \pa_mainloop - * An opaque main loop object - */ -typedef struct pa_mainloop pa_mainloop; - -/** Allocate a new main loop object */ -pa_mainloop *pa_mainloop_new(void); - -/** Free a main loop object */ -void pa_mainloop_free(pa_mainloop* m); - - -/** Prepare for a single iteration of the main loop. Returns a negative value -on error or exit request. timeout specifies a maximum timeout for the subsequent -poll, or -1 for blocking behaviour. Defer events are also dispatched when this -function is called. On success returns the number of source dispatched in this -iteration.*/ -int pa_mainloop_prepare(pa_mainloop *m, int timeout); -/** Execute the previously prepared poll. Returns a negative value on error.*/ -int pa_mainloop_poll(pa_mainloop *m); -/** Dispatch timeout and io events from the previously executed poll. Returns -a negative value on error. On success returns the number of source dispatched. */ -int pa_mainloop_dispatch(pa_mainloop *m); - -/** Return the return value as specified with the main loop's quit() routine. */ -int pa_mainloop_get_retval(pa_mainloop *m); - -/** Run a single iteration of the main loop. This is a convenience function -for pa_mainloop_prepare(), pa_mainloop_poll() and pa_mainloop_dispatch(). -Returns a negative value on error or exit request. If block is nonzero, -block for events if none are queued. Optionally return the return value as -specified with the main loop's quit() routine in the integer variable retval points -to. On success returns the number of source dispatched in this iteration. */ -int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval); - -/** Run unlimited iterations of the main loop object until the main loop's quit() routine is called. */ -int pa_mainloop_run(pa_mainloop *m, int *retval); - -/** Return the abstract main loop abstraction layer vtable for this main loop. This calls pa_mainloop_iterate() iteratively.*/ -pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m); - -/** Return non-zero when there are any deferred events pending. \since 0.5 */ -int pa_mainloop_deferred_pending(pa_mainloop *m); - -/** Shutdown the main loop */ -void pa_mainloop_quit(pa_mainloop *m, int r); - -/** Interrupt a running poll (for threaded systems) */ -void pa_mainloop_wakeup(pa_mainloop *m); - -PA_C_DECL_END - -#endif diff --git a/polyp/mcalign-test.c b/polyp/mcalign-test.c deleted file mode 100644 index c151d8f2..00000000 --- a/polyp/mcalign-test.c +++ /dev/null @@ -1,96 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "util.h" -#include "mcalign.h" -#include "gccmacro.h" - -/* A simple program for testing pa_mcalign */ - -int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { - pa_mcalign *a = pa_mcalign_new(11, NULL); - pa_memchunk c; - - pa_memchunk_reset(&c); - - srand(time(NULL)); - - for (;;) { - ssize_t r; - size_t l; - - if (!c.memblock) { - c.memblock = pa_memblock_new(2048, NULL); - c.index = c.length = 0; - } - - assert(c.index < c.memblock->length); - - l = c.memblock->length - c.index; - - l = l <= 1 ? l : rand() % (l-1) +1 ; - - if ((r = read(STDIN_FILENO, (uint8_t*) c.memblock->data + c.index, l)) <= 0) { - fprintf(stderr, "read() failed: %s\n", r < 0 ? strerror(errno) : "EOF"); - break; - } - - c.length = r; - pa_mcalign_push(a, &c); - fprintf(stderr, "Read %d bytes\n", r); - - c.index += r; - - if (c.index >= c.memblock->length) { - pa_memblock_unref(c.memblock); - pa_memchunk_reset(&c); - } - - for (;;) { - pa_memchunk t; - - if (pa_mcalign_pop(a, &t) < 0) - break; - - pa_loop_write(STDOUT_FILENO, (uint8_t*) t.memblock->data + t.index, t.length); - fprintf(stderr, "Wrote %lu bytes.\n", (unsigned long) t.length); - - pa_memblock_unref(t.memblock); - } - } - - pa_mcalign_free(a); - - if (c.memblock) - pa_memblock_unref(c.memblock); -} diff --git a/polyp/mcalign.c b/polyp/mcalign.c deleted file mode 100644 index 4d765625..00000000 --- a/polyp/mcalign.c +++ /dev/null @@ -1,188 +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 -#endif - -#include -#include -#include -#include - -#include "mcalign.h" -#include "xmalloc.h" - -struct pa_mcalign { - size_t base; - pa_memchunk leftover, current; - pa_memblock_stat *memblock_stat; -}; - -pa_mcalign *pa_mcalign_new(size_t base, pa_memblock_stat *s) { - pa_mcalign *m; - assert(base); - - m = pa_xnew(pa_mcalign, 1); - m->base = base; - pa_memchunk_reset(&m->leftover); - pa_memchunk_reset(&m->current); - m->memblock_stat = s; - - return m; -} - -void pa_mcalign_free(pa_mcalign *m) { - assert(m); - - if (m->leftover.memblock) - pa_memblock_unref(m->leftover.memblock); - - if (m->current.memblock) - pa_memblock_unref(m->current.memblock); - - pa_xfree(m); -} - -void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) { - assert(m && c && c->memblock && c->length); - - /* Append to the leftover memory block */ - if (m->leftover.memblock) { - assert(!m->current.memblock); - - /* Try to merge */ - if (m->leftover.memblock == c->memblock && - m->leftover.index + m->leftover.length == c->index) { - - /* Merge */ - m->leftover.length += c->length; - - /* If the new chunk is larger than m->base, move it to current */ - if (m->leftover.length >= m->base) { - m->current = m->leftover; - pa_memchunk_reset(&m->leftover); - } - - } else { - size_t l; - - /* We have to copy */ - assert(m->leftover.length < m->base); - l = m->base - m->leftover.length; - - if (l > c->length) - l = c->length; - - /* Can we use the current block? */ - pa_memchunk_make_writable(&m->leftover, m->memblock_stat, m->base); - - memcpy((uint8_t*) m->leftover.memblock->data + m->leftover.index + m->leftover.length, (uint8_t*) c->memblock->data + c->index, l); - m->leftover.length += l; - - assert(m->leftover.length <= m->base && m->leftover.length <= m->leftover.memblock->length); - - if (c->length > l) { - /* Save the remainder of the memory block */ - m->current = *c; - m->current.index += l; - m->current.length -= l; - pa_memblock_ref(m->current.memblock); - } - } - } else { - assert(!m->leftover.memblock && !m->current.memblock); - - /* Nothing to merge or copy, just store it */ - - if (c->length >= m->base) - m->current = *c; - else - m->leftover = *c; - - pa_memblock_ref(c->memblock); - } -} - -int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) { - assert(m && c); - - /* First test if there's a leftover memory block available */ - if (m->leftover.memblock) { - assert(m->leftover.length > 0 && m->leftover.length <= m->base); - - /* The leftover memory block is not yet complete */ - if (m->leftover.length < m->base) - return -1; - - /* Return the leftover memory block */ - *c = m->leftover; - pa_memchunk_reset(&m->leftover); - - /* If the current memblock is too small move it the leftover */ - if (m->current.memblock && m->current.length < m->base) { - m->leftover = m->current; - pa_memchunk_reset(&m->current); - } - - return 0; - } - - /* Now let's see if there is other data available */ - if (m->current.memblock) { - size_t l; - assert(m->current.length >= m->base); - - /* The length of the returned memory block */ - l = m->current.length; - l /= m->base; - l *= m->base; - assert(l > 0); - - /* Prepare the returned block */ - *c = m->current; - pa_memblock_ref(c->memblock); - c->length = l; - - /* Drop that from the current memory block */ - assert(l <= m->current.length); - m->current.index += l; - m->current.length -= l; - - /* In case the whole block was dropped ... */ - if (m->current.length == 0) - pa_memblock_unref(m->current.memblock); - else { - /* Move the raimainder to leftover */ - assert(m->current.length < m->base && !m->leftover.memblock); - - m->leftover = m->current; - } - - pa_memchunk_reset(&m->current); - - return 0; - } - - /* There's simply nothing */ - return -1; - -} diff --git a/polyp/mcalign.h b/polyp/mcalign.h deleted file mode 100644 index 5de75bc7..00000000 --- a/polyp/mcalign.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef foomcalignhfoo -#define foomcalignhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "memblock.h" -#include "memchunk.h" - -/* An alignment object, used for aligning memchunks to multiples of - * the frame size. */ - -/* Method of operation: the user creates a new mcalign object by - * calling pa_mcalign_new() with the appropriate aligning - * granularity. After that he may call pa_mcalign_push() for an input - * memchunk. After exactly one memchunk the user has to call - * pa_mcalign_pop() until it returns -1. If pa_mcalign_pop() returns - * 0, the memchunk *c is valid and aligned to the granularity. Some - * pseudocode illustrating this: - * - * pa_mcalign *a = pa_mcalign_new(4, NULL); - * - * for (;;) { - * pa_memchunk input; - * - * ... fill input ... - * - * pa_mcalign_push(m, &input); - * pa_memblock_unref(input.memblock); - * - * for (;;) { - * pa_memchunk output; - * - * if (pa_mcalign_pop(m, &output) < 0) - * break; - * - * ... consume output ... - * - * pa_memblock_unref(output.memblock); - * } - * } - * - * pa_memchunk_free(a); - * */ - -typedef struct pa_mcalign pa_mcalign; - -pa_mcalign *pa_mcalign_new(size_t base, pa_memblock_stat *s); -void pa_mcalign_free(pa_mcalign *m); - -/* Push a new memchunk into the aligner. The caller of this routine - * has to free the memchunk by himself. */ -void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c); - -/* Pop a new memchunk from the aligner. Returns 0 when sucessful, - * nonzero otherwise. */ -int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c); - -#endif diff --git a/polyp/memblock.c b/polyp/memblock.c deleted file mode 100644 index 8da53525..00000000 --- a/polyp/memblock.c +++ /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 -#endif - -#include -#include -#include -#include - -#include "memblock.h" -#include "xmalloc.h" - -static void stat_add(pa_memblock*m, pa_memblock_stat *s) { - assert(m); - - if (!s) { - m->stat = NULL; - return; - } - - m->stat = pa_memblock_stat_ref(s); - s->total++; - s->allocated++; - s->total_size += m->length; - s->allocated_size += m->length; -} - -static void stat_remove(pa_memblock *m) { - assert(m); - - if (!m->stat) - return; - - m->stat->total--; - m->stat->total_size -= m->length; - - pa_memblock_stat_unref(m->stat); - m->stat = NULL; -} - -pa_memblock *pa_memblock_new(size_t length, pa_memblock_stat*s) { - pa_memblock *b = pa_xmalloc(sizeof(pa_memblock)+length); - b->type = PA_MEMBLOCK_APPENDED; - b->ref = 1; - b->length = length; - b->data = b+1; - b->free_cb = NULL; - b->read_only = 0; - stat_add(b, s); - return b; -} - -pa_memblock *pa_memblock_new_dynamic(void *d, size_t length, pa_memblock_stat*s) { - pa_memblock *b = pa_xmalloc(sizeof(pa_memblock)); - b->type = PA_MEMBLOCK_DYNAMIC; - b->ref = 1; - b->length = length; - b->data = d; - b->free_cb = NULL; - b->read_only = 0; - stat_add(b, s); - return b; -} - -pa_memblock *pa_memblock_new_fixed(void *d, size_t length, int read_only, pa_memblock_stat*s) { - pa_memblock *b = pa_xmalloc(sizeof(pa_memblock)); - b->type = PA_MEMBLOCK_FIXED; - b->ref = 1; - b->length = length; - b->data = d; - b->free_cb = NULL; - b->read_only = read_only; - stat_add(b, s); - return b; -} - -pa_memblock *pa_memblock_new_user(void *d, size_t length, void (*free_cb)(void *p), int read_only, pa_memblock_stat*s) { - pa_memblock *b; - assert(d && length && free_cb); - b = pa_xmalloc(sizeof(pa_memblock)); - b->type = PA_MEMBLOCK_USER; - b->ref = 1; - b->length = length; - b->data = d; - b->free_cb = free_cb; - b->read_only = read_only; - stat_add(b, s); - return b; -} - -pa_memblock* pa_memblock_ref(pa_memblock*b) { - assert(b && b->ref >= 1); - b->ref++; - return b; -} - -void pa_memblock_unref(pa_memblock*b) { - assert(b && b->ref >= 1); - - if ((--(b->ref)) == 0) { - stat_remove(b); - - if (b->type == PA_MEMBLOCK_USER) { - assert(b->free_cb); - b->free_cb(b->data); - } else if (b->type == PA_MEMBLOCK_DYNAMIC) - pa_xfree(b->data); - - pa_xfree(b); - } -} - -void pa_memblock_unref_fixed(pa_memblock *b) { - assert(b && b->ref >= 1 && b->type == PA_MEMBLOCK_FIXED); - - if (b->ref == 1) - pa_memblock_unref(b); - else { - b->data = pa_xmemdup(b->data, b->length); - b->type = PA_MEMBLOCK_DYNAMIC; - b->ref--; - } -} - -pa_memblock_stat* pa_memblock_stat_new(void) { - pa_memblock_stat *s; - - s = pa_xmalloc(sizeof(pa_memblock_stat)); - s->ref = 1; - s->total = s->total_size = s->allocated = s->allocated_size = 0; - - return s; -} - -void pa_memblock_stat_unref(pa_memblock_stat *s) { - assert(s && s->ref >= 1); - - if (!(--(s->ref))) { - assert(!s->total); - pa_xfree(s); - } -} - -pa_memblock_stat * pa_memblock_stat_ref(pa_memblock_stat *s) { - assert(s); - s->ref++; - return s; -} diff --git a/polyp/memblock.h b/polyp/memblock.h deleted file mode 100644 index c5751406..00000000 --- a/polyp/memblock.h +++ /dev/null @@ -1,87 +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 -#include - -/* A pa_memblock is a reference counted memory block. Polypaudio - * passed references to pa_memblocks around instead of copying - * data. See pa_memchunk for a structure that describes parts of - * memory blocks. */ - -/* The type of memory this block points to */ -typedef enum pa_memblock_type { - PA_MEMBLOCK_FIXED, /* data is a pointer to fixed memory that needs not to be freed */ - PA_MEMBLOCK_APPENDED, /* The most common kind: the data is appended to the memory block */ - PA_MEMBLOCK_DYNAMIC, /* data is a pointer to some memory allocated with pa_xmalloc() */ - PA_MEMBLOCK_USER /* User supplied memory, to be freed with free_cb */ -} pa_memblock_type_t; - -/* A structure of keeping memory block statistics */ -/* Maintains statistics about memory blocks */ -typedef struct pa_memblock_stat { - int ref; - unsigned total; - unsigned total_size; - unsigned allocated; - unsigned allocated_size; -} pa_memblock_stat; - -typedef struct pa_memblock { - pa_memblock_type_t type; - unsigned ref; /* the reference counter */ - int read_only; /* boolean */ - size_t length; - void *data; - void (*free_cb)(void *p); /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */ - pa_memblock_stat *stat; -} pa_memblock; - -/* Allocate a new memory block of type PA_MEMBLOCK_APPENDED */ -pa_memblock *pa_memblock_new(size_t length, pa_memblock_stat*s); - -/* Allocate a new memory block of type PA_MEMBLOCK_DYNAMIC. The pointer data is to be maintained be the memory block */ -pa_memblock *pa_memblock_new_dynamic(void *data, size_t length, pa_memblock_stat*s); - -/* Allocate a new memory block of type PA_MEMBLOCK_FIXED */ -pa_memblock *pa_memblock_new_fixed(void *data, size_t length, int read_only, pa_memblock_stat*s); - -/* Allocate a new memory block of type PA_MEMBLOCK_USER */ -pa_memblock *pa_memblock_new_user(void *data, size_t length, void (*free_cb)(void *p), int read_only, pa_memblock_stat*s); - -void pa_memblock_unref(pa_memblock*b); -pa_memblock* pa_memblock_ref(pa_memblock*b); - -/* This special unref function has to be called by the owner of the -memory of a static memory block when he wants to release all -references to the memory. This causes the memory to be copied and -converted into a PA_MEMBLOCK_DYNAMIC type memory block */ -void pa_memblock_unref_fixed(pa_memblock*b); - - -pa_memblock_stat* pa_memblock_stat_new(void); -void pa_memblock_stat_unref(pa_memblock_stat *s); -pa_memblock_stat * pa_memblock_stat_ref(pa_memblock_stat *s); - -#endif diff --git a/polyp/memblockq.c b/polyp/memblockq.c deleted file mode 100644 index ba6b76ea..00000000 --- a/polyp/memblockq.c +++ /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 -#endif - -#include -#include -#include -#include -#include -#include - -#include "memblockq.h" -#include "xmalloc.h" -#include "log.h" -#include "mcalign.h" - -struct memblock_list { - struct memblock_list *next, *prev; - pa_memchunk chunk; -}; - -struct pa_memblockq { - struct memblock_list *blocks, *blocks_tail; - unsigned n_blocks; - size_t current_length, maxlength, tlength, base, prebuf, orig_prebuf, minreq; - pa_mcalign *mcalign; - pa_memblock_stat *memblock_stat; -}; - -pa_memblockq* pa_memblockq_new(size_t maxlength, size_t tlength, size_t base, size_t prebuf, size_t minreq, pa_memblock_stat *s) { - pa_memblockq* bq; - assert(maxlength && base && maxlength); - - bq = pa_xmalloc(sizeof(pa_memblockq)); - bq->blocks = bq->blocks_tail = 0; - bq->n_blocks = 0; - - bq->current_length = 0; - - pa_log_debug(__FILE__": memblockq requested: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", maxlength, tlength, base, prebuf, minreq); - - bq->base = base; - - bq->maxlength = ((maxlength+base-1)/base)*base; - assert(bq->maxlength >= base); - - bq->tlength = ((tlength+base-1)/base)*base; - if (!bq->tlength || bq->tlength >= bq->maxlength) - bq->tlength = bq->maxlength; - - bq->minreq = (minreq/base)*base; - if (bq->minreq == 0) - bq->minreq = 1; - - bq->prebuf = (prebuf == (size_t) -1) ? bq->maxlength/2 : prebuf; - bq->prebuf = (bq->prebuf/base)*base; - if (bq->prebuf > bq->maxlength) - bq->prebuf = bq->maxlength; - - if (bq->prebuf > bq->tlength - bq->minreq) - bq->prebuf = bq->tlength - bq->minreq; - - bq->orig_prebuf = bq->prebuf; - - pa_log_debug(__FILE__": memblockq sanitized: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", bq->maxlength, bq->tlength, bq->base, bq->prebuf, bq->minreq); - - bq->mcalign = NULL; - - bq->memblock_stat = s; - - return bq; -} - -void pa_memblockq_free(pa_memblockq* bq) { - assert(bq); - - pa_memblockq_flush(bq); - - if (bq->mcalign) - pa_mcalign_free(bq->mcalign); - - pa_xfree(bq); -} - -void pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta) { - struct memblock_list *q; - assert(bq && chunk && chunk->memblock && chunk->length && (chunk->length % bq->base) == 0); - - pa_memblockq_seek(bq, delta); - - if (bq->blocks_tail && bq->blocks_tail->chunk.memblock == chunk->memblock) { - /* Try to merge memory chunks */ - - if (bq->blocks_tail->chunk.index+bq->blocks_tail->chunk.length == chunk->index) { - bq->blocks_tail->chunk.length += chunk->length; - bq->current_length += chunk->length; - return; - } - } - - q = pa_xmalloc(sizeof(struct memblock_list)); - - q->chunk = *chunk; - pa_memblock_ref(q->chunk.memblock); - assert(q->chunk.index+q->chunk.length <= q->chunk.memblock->length); - q->next = NULL; - if ((q->prev = bq->blocks_tail)) - bq->blocks_tail->next = q; - else - bq->blocks = q; - - bq->blocks_tail = q; - - bq->n_blocks++; - bq->current_length += chunk->length; - - pa_memblockq_shorten(bq, bq->maxlength); -} - -int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) { - assert(bq && chunk); - - if (!bq->blocks || bq->current_length < bq->prebuf) - return -1; - - bq->prebuf = 0; - - *chunk = bq->blocks->chunk; - pa_memblock_ref(chunk->memblock); - - return 0; -} - -void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length) { - assert(bq && chunk && length); - - if (!bq->blocks || memcmp(&bq->blocks->chunk, chunk, sizeof(pa_memchunk))) - return; - - assert(length <= bq->blocks->chunk.length); - pa_memblockq_skip(bq, length); -} - -static void remove_block(pa_memblockq *bq, struct memblock_list *q) { - assert(bq && q); - - if (q->prev) - q->prev->next = q->next; - else { - assert(bq->blocks == q); - bq->blocks = q->next; - } - - if (q->next) - q->next->prev = q->prev; - else { - assert(bq->blocks_tail == q); - bq->blocks_tail = q->prev; - } - - pa_memblock_unref(q->chunk.memblock); - pa_xfree(q); - - bq->n_blocks--; -} - -void pa_memblockq_skip(pa_memblockq *bq, size_t length) { - assert(bq && length && (length % bq->base) == 0); - - while (length > 0) { - size_t l = length; - assert(bq->blocks && bq->current_length >= length); - - if (l > bq->blocks->chunk.length) - l = bq->blocks->chunk.length; - - bq->blocks->chunk.index += l; - bq->blocks->chunk.length -= l; - bq->current_length -= l; - - if (!bq->blocks->chunk.length) - remove_block(bq, bq->blocks); - - length -= l; - } -} - -void pa_memblockq_shorten(pa_memblockq *bq, size_t length) { - size_t l; - assert(bq); - - if (bq->current_length <= length) - return; - - /*pa_log(__FILE__": Warning! pa_memblockq_shorten()\n");*/ - - l = bq->current_length - length; - l /= bq->base; - l *= bq->base; - - pa_memblockq_skip(bq, l); -} - - -void pa_memblockq_empty(pa_memblockq *bq) { - assert(bq); - pa_memblockq_shorten(bq, 0); -} - -int pa_memblockq_is_readable(pa_memblockq *bq) { - assert(bq); - - return bq->current_length && (bq->current_length >= bq->prebuf); -} - -int pa_memblockq_is_writable(pa_memblockq *bq, size_t length) { - assert(bq); - - return bq->current_length + length <= bq->tlength; -} - -uint32_t pa_memblockq_get_length(pa_memblockq *bq) { - assert(bq); - return bq->current_length; -} - -uint32_t pa_memblockq_missing(pa_memblockq *bq) { - size_t l; - assert(bq); - - if (bq->current_length >= bq->tlength) - return 0; - - l = bq->tlength - bq->current_length; - assert(l); - - return (l >= bq->minreq) ? l : 0; -} - -void pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta) { - pa_memchunk rchunk; - assert(bq && chunk && bq->base); - - if (bq->base == 1) { - pa_memblockq_push(bq, chunk, delta); - return; - } - - if (!bq->mcalign) { - bq->mcalign = pa_mcalign_new(bq->base, bq->memblock_stat); - assert(bq->mcalign); - } - - pa_mcalign_push(bq->mcalign, chunk); - - while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) { - pa_memblockq_push(bq, &rchunk, delta); - pa_memblock_unref(rchunk.memblock); - delta = 0; - } -} - -uint32_t pa_memblockq_get_minreq(pa_memblockq *bq) { - assert(bq); - return bq->minreq; -} - -void pa_memblockq_prebuf_disable(pa_memblockq *bq) { - assert(bq); - bq->prebuf = 0; -} - -void pa_memblockq_prebuf_reenable(pa_memblockq *bq) { - assert(bq); - bq->prebuf = bq->orig_prebuf; -} - -void pa_memblockq_seek(pa_memblockq *bq, size_t length) { - assert(bq); - - if (!length) - return; - - while (length >= bq->base) { - size_t l = length; - if (!bq->current_length) - return; - - assert(bq->blocks_tail); - - if (l > bq->blocks_tail->chunk.length) - l = bq->blocks_tail->chunk.length; - - bq->blocks_tail->chunk.length -= l; - bq->current_length -= l; - - if (bq->blocks_tail->chunk.length == 0) - remove_block(bq, bq->blocks); - - length -= l; - } -} - -void pa_memblockq_flush(pa_memblockq *bq) { - struct memblock_list *l; - assert(bq); - - while ((l = bq->blocks)) { - bq->blocks = l->next; - pa_memblock_unref(l->chunk.memblock); - pa_xfree(l); - } - - bq->blocks_tail = NULL; - bq->n_blocks = 0; - bq->current_length = 0; -} - -uint32_t pa_memblockq_get_tlength(pa_memblockq *bq) { - assert(bq); - return bq->tlength; -} diff --git a/polyp/memblockq.h b/polyp/memblockq.h deleted file mode 100644 index 1695daba..00000000 --- a/polyp/memblockq.h +++ /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 - -#include "memblock.h" -#include "memchunk.h" - -/* A memblockq is a queue of pa_memchunks (yepp, the name is not - * perfect). It is similar to the ring buffers used by most other - * audio software. In contrast to a ring buffer this memblockq data - * type doesn't need to copy any data around, it just maintains - * references to reference counted memory blocks. */ - -typedef struct pa_memblockq pa_memblockq; - -/* Parameters: - - maxlength: maximum length of queue. If more data is pushed into the queue, data from the front is dropped - - length: the target length of the queue. - - base: a base value for all metrics. Only multiples of this value are popped from the queue - - prebuf: before passing the first byte out, make sure that enough bytes are in the queue - - minreq: pa_memblockq_missing() will only return values greater than this value -*/ -pa_memblockq* pa_memblockq_new(size_t maxlength, - size_t tlength, - size_t base, - size_t prebuf, - size_t minreq, - pa_memblock_stat *s); -void pa_memblockq_free(pa_memblockq*bq); - -/* Push a new memory chunk into the queue. Optionally specify a value for future cancellation. */ -void pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta); - -/* Same as pa_memblockq_push(), however chunks are filtered through a mcalign object, and thus aligned to multiples of base */ -void pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta); - -/* Return a copy of the next memory chunk in the queue. It is not removed from the queue */ -int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk); - -/* Drop the specified bytes from the queue, only valid aufter pa_memblockq_peek() */ -void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length); - -/* Drop the specified bytes from the queue */ -void pa_memblockq_skip(pa_memblockq *bq, size_t length); - -/* Shorten the pa_memblockq to the specified length by dropping data at the end of the queue */ -void pa_memblockq_shorten(pa_memblockq *bq, size_t length); - -/* Empty the pa_memblockq */ -void pa_memblockq_empty(pa_memblockq *bq); - -/* Test if the pa_memblockq is currently readable, that is, more data than base */ -int pa_memblockq_is_readable(pa_memblockq *bq); - -/* Test if the pa_memblockq is currently writable for the specified amount of bytes */ -int pa_memblockq_is_writable(pa_memblockq *bq, size_t length); - -/* Return the length of the queue in bytes */ -uint32_t pa_memblockq_get_length(pa_memblockq *bq); - -/* Return how many bytes are missing in queue to the specified fill amount */ -uint32_t pa_memblockq_missing(pa_memblockq *bq); - -/* Returns the minimal request value */ -uint32_t pa_memblockq_get_minreq(pa_memblockq *bq); - -/* Force disabling of pre-buf even when the pre-buffer is not yet filled */ -void pa_memblockq_prebuf_disable(pa_memblockq *bq); - -/* Reenable pre-buf to the initial level */ -void pa_memblockq_prebuf_reenable(pa_memblockq *bq); - -/* Manipulate the write pointer */ -void pa_memblockq_seek(pa_memblockq *bq, size_t delta); - -/* Flush the queue */ -void pa_memblockq_flush(pa_memblockq *bq); - -/* Get Target length */ -uint32_t pa_memblockq_get_tlength(pa_memblockq *bq); - -#endif diff --git a/polyp/memchunk.c b/polyp/memchunk.c deleted file mode 100644 index bfd74f9e..00000000 --- a/polyp/memchunk.c +++ /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 -#endif - -#include -#include -#include -#include - -#include "memchunk.h" -#include "xmalloc.h" - -void pa_memchunk_make_writable(pa_memchunk *c, pa_memblock_stat *s, size_t min) { - pa_memblock *n; - size_t l; - assert(c && c->memblock && c->memblock->ref >= 1); - - if (c->memblock->ref == 1 && !c->memblock->read_only && c->memblock->length >= c->index+min) - return; - - l = c->length; - if (l < min) - l = min; - - n = pa_memblock_new(l, s); - memcpy(n->data, (uint8_t*) c->memblock->data + c->index, c->length); - pa_memblock_unref(c->memblock); - c->memblock = n; - c->index = 0; -} - -void pa_memchunk_reset(pa_memchunk *c) { - assert(c); - - c->memblock = NULL; - c->length = c->index = 0; -} diff --git a/polyp/memchunk.h b/polyp/memchunk.h deleted file mode 100644 index 4eefc8c1..00000000 --- a/polyp/memchunk.h +++ /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 describes a part of a memblock. In contrast to the memblock, a - * memchunk is not allocated dynamically or reference counted, instead - * it is usually stored on the stack and copied around */ - -typedef struct pa_memchunk { - pa_memblock *memblock; - size_t index, length; -} pa_memchunk; - -/* Make a memchunk writable, i.e. make sure that the caller may have - * exclusive access to the memblock and it is not read_only. If needed - * the memblock in the structure is replaced by a copy. */ -void pa_memchunk_make_writable(pa_memchunk *c, pa_memblock_stat *s, size_t min); - -/* Invalidate a memchunk. This does not free the cotaining memblock, - * but sets all members to zero. */ -void pa_memchunk_reset(pa_memchunk *c); - -#endif diff --git a/polyp/modargs.c b/polyp/modargs.c deleted file mode 100644 index 07062946..00000000 --- a/polyp/modargs.c +++ /dev/null @@ -1,260 +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 -#endif - -#include -#include -#include -#include - -#include "hashmap.h" -#include "modargs.h" -#include "idxset.h" -#include "sample-util.h" -#include "namereg.h" -#include "sink.h" -#include "source.h" -#include "xmalloc.h" -#include "util.h" - -struct entry { - char *key, *value; -}; - -static int add_key_value(pa_hashmap *map, char *key, char *value, const char* const valid_keys[]) { - struct entry *e; - assert(map && key && value); - - if (valid_keys) { - const char*const* v; - for (v = valid_keys; *v; v++) - if (strcmp(*v, key) == 0) - break; - - if (!*v) { - pa_xfree(key); - pa_xfree(value); - return -1; - } - } - - e = pa_xmalloc(sizeof(struct entry)); - e->key = key; - e->value = value; - pa_hashmap_put(map, key, e); - return 0; -} - -pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) { - pa_hashmap *map = NULL; - - map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - assert(map); - - if (args) { - enum { WHITESPACE, KEY, VALUE_START, VALUE_SIMPLE, VALUE_DOUBLE_QUOTES, VALUE_TICKS } state; - const char *p, *key, *value; - size_t key_len = 0, value_len = 0; - - key = value = NULL; - state = WHITESPACE; - for (p = args; *p; p++) { - switch (state) { - case WHITESPACE: - if (*p == '=') - goto fail; - else if (!isspace(*p)) { - key = p; - state = KEY; - key_len = 1; - } - break; - case KEY: - if (*p == '=') - state = VALUE_START; - else - key_len++; - break; - case VALUE_START: - if (*p == '\'') { - state = VALUE_TICKS; - value = p+1; - value_len = 0; - } else if (*p == '"') { - state = VALUE_DOUBLE_QUOTES; - value = p+1; - value_len = 0; - } else if (isspace(*p)) { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0) - goto fail; - state = WHITESPACE; - } else { - state = VALUE_SIMPLE; - value = p; - value_len = 1; - } - break; - case VALUE_SIMPLE: - if (isspace(*p)) { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) - goto fail; - state = WHITESPACE; - } else - value_len++; - break; - case VALUE_DOUBLE_QUOTES: - if (*p == '"') { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) - goto fail; - state = WHITESPACE; - } else - value_len++; - break; - case VALUE_TICKS: - if (*p == '\'') { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) - goto fail; - state = WHITESPACE; - } else - value_len++; - break; - } - } - - if (state == VALUE_START) { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0) - goto fail; - } else if (state == VALUE_SIMPLE) { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(value), valid_keys) < 0) - goto fail; - } else if (state != WHITESPACE) - goto fail; - } - - return (pa_modargs*) map; - -fail: - - if (map) - pa_modargs_free((pa_modargs*) map); - - return NULL; -} - - -static void free_func(void *p, PA_GCC_UNUSED void*userdata) { - struct entry *e = p; - assert(e); - pa_xfree(e->key); - pa_xfree(e->value); - pa_xfree(e); -} - -void pa_modargs_free(pa_modargs*ma) { - pa_hashmap *map = (pa_hashmap*) ma; - pa_hashmap_free(map, free_func, NULL); -} - -const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *def) { - pa_hashmap *map = (pa_hashmap*) ma; - struct entry*e; - - if (!(e = pa_hashmap_get(map, key))) - return def; - - return e->value; -} - -int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value) { - const char *v; - assert(ma && key && value); - - if (!(v = pa_modargs_get_value(ma, key, NULL))) - return 0; - - if (pa_atou(v, value) < 0) - return -1; - - return 0; -} - -int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value) { - const char *v; - assert(ma && key && value); - - if (!(v = pa_modargs_get_value(ma, key, NULL))) - return 0; - - if (pa_atoi(v, value) < 0) - return -1; - - return 0; -} - -int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value) { - const char *v; - int r; - assert(ma && key && value); - - if (!(v = pa_modargs_get_value(ma, key, NULL))) - return 0; - - if (!*v) - return -1; - - if ((r = pa_parse_boolean(v)) < 0) - return -1; - - *value = r; - return 0; -} - -int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) { - const char *format; - uint32_t channels; - pa_sample_spec ss; - assert(ma && rss); - -/* DEBUG_TRAP;*/ - - ss = *rss; - if ((pa_modargs_get_value_u32(ma, "rate", &ss.rate)) < 0) - return -1; - - channels = ss.channels; - if ((pa_modargs_get_value_u32(ma, "channels", &channels)) < 0) - return -1; - ss.channels = (uint8_t) channels; - - if ((format = pa_modargs_get_value(ma, "format", NULL))) - if ((ss.format = pa_parse_sample_format(format)) < 0) - return -1; - - if (!pa_sample_spec_valid(&ss)) - return -1; - - *rss = ss; - - return 0; -} diff --git a/polyp/modargs.h b/polyp/modargs.h deleted file mode 100644 index 56605379..00000000 --- a/polyp/modargs.h +++ /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 -#include "sample.h" -#include "core.h" - -typedef struct pa_modargs pa_modargs; - -/* A generic parser for module arguments */ - -/* Parse the string args. The NULL-terminated array keys contains all valid arguments. */ -pa_modargs *pa_modargs_new(const char *args, const char* const keys[]); -void pa_modargs_free(pa_modargs*ma); - -/* Return the module argument for the specified name as a string. If - * the argument was not specified, return def instead.*/ -const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *def); - -/* Return a module argument as unsigned 32bit value in *value */ -int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value); -int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value); -int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value); - -/* Return sample spec data from the three arguments "rate", "format" and "channels" */ -int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *ss); - -#endif diff --git a/polyp/modinfo.c b/polyp/modinfo.c deleted file mode 100644 index 53440612..00000000 --- a/polyp/modinfo.c +++ /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 -#endif - -#include -#include - -#include "xmalloc.h" -#include "util.h" -#include "modinfo.h" -#include "log.h" - -#define PA_SYMBOL_AUTHOR "pa__get_author" -#define PA_SYMBOL_DESCRIPTION "pa__get_description" -#define PA_SYMBOL_USAGE "pa__get_usage" -#define PA_SYMBOL_VERSION "pa__get_version" - -pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl) { - pa_modinfo *i; - const char* (*func)(void); - assert(dl); - - i = pa_xmalloc0(sizeof(pa_modinfo)); - - if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_AUTHOR))) - i->author = pa_xstrdup(func()); - - if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_DESCRIPTION))) - i->description = pa_xstrdup(func()); - - if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_USAGE))) - i->usage = pa_xstrdup(func()); - - if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_VERSION))) - i->version = pa_xstrdup(func()); - - return i; -} - -pa_modinfo *pa_modinfo_get_by_name(const char *name) { - lt_dlhandle dl; - pa_modinfo *i; - assert(name); - - if (!(dl = lt_dlopenext(name))) { - pa_log(__FILE__": Failed to open module \"%s\": %s\n", name, lt_dlerror()); - return NULL; - } - - i = pa_modinfo_get_by_handle(dl); - lt_dlclose(dl); - - return i; -} - -void pa_modinfo_free(pa_modinfo *i) { - assert(i); - pa_xfree(i->author); - pa_xfree(i->description); - pa_xfree(i->usage); - pa_xfree(i->version); - pa_xfree(i); -} diff --git a/polyp/modinfo.h b/polyp/modinfo.h deleted file mode 100644 index 53176147..00000000 --- a/polyp/modinfo.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef foomodinfohfoo -#define foomodinfohfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -/* Some functions for reading module meta data from Polypaudio modules */ - -typedef struct pa_modinfo { - char *author; - char *description; - char *usage; - char *version; -} pa_modinfo; - -/* Read meta data from an libtool handle */ -pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl); - -/* Read meta data from a module file */ -pa_modinfo *pa_modinfo_get_by_name(const char *name); - -/* Free meta data */ -void pa_modinfo_free(pa_modinfo *i); - -#endif diff --git a/polyp/module-alsa-sink.c b/polyp/module-alsa-sink.c deleted file mode 100644 index 9aa220be..00000000 --- a/polyp/module-alsa-sink.c +++ /dev/null @@ -1,291 +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 -#endif - -#include -#include - -#ifdef HAVE_SYS_POLL_H -#include -#else -#include "poll.h" -#endif - -#include - -#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= device= format= channels= rate= fragments= fragment_size=") - -struct userdata { - snd_pcm_t *pcm_handle; - pa_sink *sink; - pa_io_event **io_events; - unsigned n_io_events; - - size_t frame_size, fragment_size; - pa_memchunk memchunk, silence; - pa_module *module; -}; - -static const char* const valid_modargs[] = { - "device", - "sink_name", - "format", - "channels", - "rate", - "fragments", - "fragment_size", - NULL -}; - -#define DEFAULT_SINK_NAME "alsa_output" -#define DEFAULT_DEVICE "default" - -static void update_usage(struct userdata *u) { - pa_module_set_used(u->module, - (u->sink ? pa_idxset_size(u->sink->inputs) : 0) + - (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0)); -} - -static void xrun_recovery(struct userdata *u) { - assert(u); - - pa_log(__FILE__": *** ALSA-XRUN (playback) ***\n"); - - if (snd_pcm_prepare(u->pcm_handle) < 0) - pa_log(__FILE__": snd_pcm_prepare() failed\n"); -} - -static void do_write(struct userdata *u) { - assert(u); - - update_usage(u); - - for (;;) { - pa_memchunk *memchunk = NULL; - snd_pcm_sframes_t frames; - - if (u->memchunk.memblock) - memchunk = &u->memchunk; - else { - if (pa_sink_render(u->sink, u->fragment_size, &u->memchunk) < 0) - memchunk = &u->silence; - else - memchunk = &u->memchunk; - } - - assert(memchunk->memblock && memchunk->memblock->data && memchunk->length && memchunk->memblock->length && (memchunk->length % u->frame_size) == 0); - - if ((frames = snd_pcm_writei(u->pcm_handle, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length / u->frame_size)) < 0) { - if (frames == -EAGAIN) - return; - - if (frames == -EPIPE) { - xrun_recovery(u); - continue; - } - - pa_log(__FILE__": snd_pcm_writei() failed\n"); - return; - } - - if (memchunk == &u->memchunk) { - size_t l = frames * u->frame_size; - memchunk->index += l; - memchunk->length -= l; - - if (memchunk->length == 0) { - pa_memblock_unref(memchunk->memblock); - memchunk->memblock = NULL; - memchunk->index = memchunk->length = 0; - } - } - - break; - } -} - -static void io_callback(pa_mainloop_api*a, pa_io_event *e, PA_GCC_UNUSED int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { - struct userdata *u = userdata; - assert(u && a && e); - - if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN) - xrun_recovery(u); - - do_write(u); -} - -static pa_usec_t sink_get_latency_cb(pa_sink *s) { - pa_usec_t r = 0; - struct userdata *u = s->userdata; - snd_pcm_sframes_t frames; - assert(s && u && u->sink); - - if (snd_pcm_delay(u->pcm_handle, &frames) < 0) { - pa_log(__FILE__": failed to get delay\n"); - s->get_latency = NULL; - return 0; - } - - if (frames < 0) - frames = 0; - - r += pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec); - - if (u->memchunk.memblock) - r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec); - - return r; -} - -int pa__init(pa_core *c, pa_module*m) { - pa_modargs *ma = NULL; - int ret = -1; - struct userdata *u = NULL; - const char *dev; - pa_sample_spec ss; - uint32_t periods, fragsize; - snd_pcm_uframes_t period_size; - size_t frame_size; - int err; - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": failed to parse module arguments\n"); - goto fail; - } - - ss = c->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &ss) < 0) { - pa_log(__FILE__": failed to parse sample specification\n"); - goto fail; - } - frame_size = pa_frame_size(&ss); - - periods = 8; - fragsize = 1024; - if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) { - pa_log(__FILE__": failed to parse buffer metrics\n"); - goto fail; - } - period_size = fragsize; - - u = pa_xmalloc0(sizeof(struct userdata)); - m->userdata = u; - u->module = m; - - snd_config_update_free_global(); - if ((err = snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { - pa_log(__FILE__": Error opening PCM device %s: %s\n", dev, snd_strerror(err)); - goto fail; - } - - if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &period_size)) < 0) { - pa_log(__FILE__": Failed to set hardware parameters: %s\n", snd_strerror(err)); - goto fail; - } - - u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL); - assert(u->sink); - - u->sink->get_latency = sink_get_latency_cb; - u->sink->userdata = u; - pa_sink_set_owner(u->sink, m); - u->sink->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev); - - if (pa_create_io_events(u->pcm_handle, c->mainloop, &u->io_events, &u->n_io_events, io_callback, u) < 0) { - pa_log(__FILE__": failed to obtain file descriptors\n"); - goto fail; - } - - u->frame_size = frame_size; - u->fragment_size = period_size; - - pa_log_info(__FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size); - - u->silence.memblock = pa_memblock_new(u->silence.length = u->fragment_size, c->memblock_stat); - assert(u->silence.memblock); - pa_silence_memblock(u->silence.memblock, &ss); - u->silence.index = 0; - - u->memchunk.memblock = NULL; - u->memchunk.index = u->memchunk.length = 0; - - ret = 0; - -finish: - if (ma) - pa_modargs_free(ma); - - return ret; - -fail: - - if (u) - pa__done(c, m); - - goto finish; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata *u; - assert(c && m); - - if (!(u = m->userdata)) - return; - - if (u->sink) { - pa_sink_disconnect(u->sink); - pa_sink_unref(u->sink); - } - - if (u->io_events) - pa_free_io_events(c->mainloop, u->io_events, u->n_io_events); - - if (u->pcm_handle) { - snd_pcm_drop(u->pcm_handle); - snd_pcm_close(u->pcm_handle); - } - - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); - if (u->silence.memblock) - pa_memblock_unref(u->silence.memblock); - - pa_xfree(u); -} - diff --git a/polyp/module-alsa-source.c b/polyp/module-alsa-source.c deleted file mode 100644 index efc84efe..00000000 --- a/polyp/module-alsa-source.c +++ /dev/null @@ -1,277 +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 -#endif - -#include -#include - -#ifdef HAVE_SYS_POLL_H -#include -#else -#include "poll.h" -#endif - -#include - -#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= device= format= channels= rate= fragments= fragment_size=") - -struct userdata { - snd_pcm_t *pcm_handle; - pa_source *source; - pa_io_event **io_events; - unsigned n_io_events; - - size_t frame_size, fragment_size; - pa_memchunk memchunk; - pa_module *module; -}; - -static const char* const valid_modargs[] = { - "device", - "source_name", - "channels", - "rate", - "format", - "fragments", - "fragment_size", - NULL -}; - -#define DEFAULT_SOURCE_NAME "alsa_input" -#define DEFAULT_DEVICE "hw:0,0" - -static void update_usage(struct userdata *u) { - pa_module_set_used(u->module, - (u->source ? pa_idxset_size(u->source->outputs) : 0)); -} - -static void xrun_recovery(struct userdata *u) { - assert(u); - - pa_log(__FILE__": *** ALSA-XRUN (capture) ***\n"); - - if (snd_pcm_prepare(u->pcm_handle) < 0) - pa_log(__FILE__": snd_pcm_prepare() failed\n"); -} - -static void do_read(struct userdata *u) { - assert(u); - - update_usage(u); - - for (;;) { - pa_memchunk post_memchunk; - snd_pcm_sframes_t frames; - size_t l; - - if (!u->memchunk.memblock) { - u->memchunk.memblock = pa_memblock_new(u->memchunk.length = u->fragment_size, u->source->core->memblock_stat); - u->memchunk.index = 0; - } - - assert(u->memchunk.memblock && u->memchunk.memblock->data && u->memchunk.length && u->memchunk.memblock->length && (u->memchunk.length % u->frame_size) == 0); - - if ((frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) { - if (frames == -EAGAIN) - return; - - if (frames == -EPIPE) { - xrun_recovery(u); - continue; - } - - pa_log(__FILE__": snd_pcm_readi() failed: %s\n", strerror(-frames)); - return; - } - - l = frames * u->frame_size; - - post_memchunk = u->memchunk; - post_memchunk.length = l; - - pa_source_post(u->source, &post_memchunk); - - u->memchunk.index += l; - u->memchunk.length -= l; - - if (u->memchunk.length == 0) { - pa_memblock_unref(u->memchunk.memblock); - u->memchunk.memblock = NULL; - u->memchunk.index = u->memchunk.length = 0; - } - - break; - } -} - -static void io_callback(pa_mainloop_api*a, pa_io_event *e, PA_GCC_UNUSED int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { - struct userdata *u = userdata; - assert(u && a && e); - - if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN) - xrun_recovery(u); - - do_read(u); -} - -static pa_usec_t source_get_latency_cb(pa_source *s) { - struct userdata *u = s->userdata; - snd_pcm_sframes_t frames; - assert(s && u && u->source); - - if (snd_pcm_delay(u->pcm_handle, &frames) < 0) { - pa_log(__FILE__": failed to get delay\n"); - s->get_latency = NULL; - return 0; - } - - return pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec); -} - -int pa__init(pa_core *c, pa_module*m) { - pa_modargs *ma = NULL; - int ret = -1; - struct userdata *u = NULL; - const char *dev; - pa_sample_spec ss; - unsigned periods, fragsize; - snd_pcm_uframes_t period_size; - size_t frame_size; - int err; - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": failed to parse module arguments\n"); - goto fail; - } - - ss = c->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &ss) < 0) { - pa_log(__FILE__": failed to parse sample specification\n"); - goto fail; - } - frame_size = pa_frame_size(&ss); - - periods = 12; - fragsize = 1024; - if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) { - pa_log(__FILE__": failed to parse buffer metrics\n"); - goto fail; - } - period_size = fragsize; - - u = pa_xmalloc0(sizeof(struct userdata)); - m->userdata = u; - u->module = m; - - snd_config_update_free_global(); - if ((err = snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) { - pa_log(__FILE__": Error opening PCM device %s: %s\n", dev, snd_strerror(err)); - goto fail; - } - - if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &period_size)) < 0) { - pa_log(__FILE__": Failed to set hardware parameters: %s\n", snd_strerror(err)); - goto fail; - } - - u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL); - assert(u->source); - - u->source->userdata = u; - u->source->get_latency = source_get_latency_cb; - pa_source_set_owner(u->source, m); - u->source->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev); - - if (pa_create_io_events(u->pcm_handle, c->mainloop, &u->io_events, &u->n_io_events, io_callback, u) < 0) { - pa_log(__FILE__": failed to obtain file descriptors\n"); - goto fail; - } - - u->frame_size = frame_size; - u->fragment_size = period_size; - - pa_log(__FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size); - - u->memchunk.memblock = NULL; - u->memchunk.index = u->memchunk.length = 0; - - snd_pcm_start(u->pcm_handle); - - ret = 0; - -finish: - if (ma) - pa_modargs_free(ma); - - return ret; - -fail: - - if (u) - pa__done(c, m); - - goto finish; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata *u; - assert(c && m); - - if (!(u = m->userdata)) - return; - - if (u->source) { - pa_source_disconnect(u->source); - pa_source_unref(u->source); - } - - if (u->io_events) - pa_free_io_events(c->mainloop, u->io_events, u->n_io_events); - - if (u->pcm_handle) { - snd_pcm_drop(u->pcm_handle); - snd_pcm_close(u->pcm_handle); - } - - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); - - pa_xfree(u); -} - diff --git a/polyp/module-cli.c b/polyp/module-cli.c deleted file mode 100644 index db0e895c..00000000 --- a/polyp/module-cli.c +++ /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. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#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(pa_cli*c, void *userdata) { - pa_module *m = userdata; - assert(c && m); - - pa_module_unload_request(m); -} - -int pa__init(pa_core *c, pa_module*m) { - pa_iochannel *io; - assert(c && m); - - if (c->running_as_daemon) { - pa_log_info(__FILE__": Running as daemon so won't load this module.\n"); - return 0; - } - - if (m->argument) { - pa_log(__FILE__": module doesn't accept arguments.\n"); - return -1; - } - - if (pa_stdio_acquire() < 0) { - pa_log(__FILE__": STDIN/STDUSE already in use.\n"); - return -1; - } - - io = pa_iochannel_new(c->mainloop, STDIN_FILENO, STDOUT_FILENO); - assert(io); - pa_iochannel_set_noclose(io, 1); - - m->userdata = pa_cli_new(c, io, m); - assert(m->userdata); - - pa_cli_set_eof_callback(m->userdata, eof_cb, m); - - return 0; -} - -void pa__done(pa_core *c, pa_module*m) { - assert(c && m); - - if (c->running_as_daemon == 0) { - pa_cli_free(m->userdata); - pa_stdio_release(); - } -} diff --git a/polyp/module-combine.c b/polyp/module-combine.c deleted file mode 100644 index 0c21c2f5..00000000 --- a/polyp/module-combine.c +++ /dev/null @@ -1,393 +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 -#endif - -#include -#include - -#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= master= slaves= adjust_time= resample_method=") - -#define DEFAULT_SINK_NAME "combined" -#define MEMBLOCKQ_MAXLENGTH (1024*170) -#define RENDER_SIZE (1024*10) - -#define DEFAULT_ADJUST_TIME 20 - -static const char* const valid_modargs[] = { - "sink_name", - "master", - "slaves", - "adjust_time", - "resample_method", - NULL -}; - -struct output { - struct userdata *userdata; - pa_sink_input *sink_input; - size_t counter; - pa_memblockq *memblockq; - pa_usec_t total_latency; - PA_LLIST_FIELDS(struct output); -}; - -struct userdata { - pa_module *module; - pa_core *core; - pa_sink *sink; - unsigned n_outputs; - struct output *master; - pa_time_event *time_event; - uint32_t adjust_time; - - PA_LLIST_HEAD(struct output, outputs); -}; - -static void output_free(struct output *o); -static void clear_up(struct userdata *u); - -static void update_usage(struct userdata *u) { - pa_module_set_used(u->module, - (u->sink ? pa_idxset_size(u->sink->inputs) : 0) + - (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0)); -} - - -static void adjust_rates(struct userdata *u) { - struct output *o; - pa_usec_t max_sink_latency = 0, min_total_latency = (pa_usec_t) -1, target_latency; - uint32_t base_rate; - assert(u && u->sink); - - for (o = u->outputs; o; o = o->next) { - uint32_t sink_latency = o->sink_input->sink ? pa_sink_get_latency(o->sink_input->sink) : 0; - - o->total_latency = sink_latency + pa_sink_input_get_latency(o->sink_input); - - if (sink_latency > max_sink_latency) - max_sink_latency = sink_latency; - - if (o->total_latency < min_total_latency) - min_total_latency = o->total_latency; - } - - assert(min_total_latency != (pa_usec_t) -1); - - target_latency = max_sink_latency > min_total_latency ? max_sink_latency : min_total_latency; - - pa_log_info(__FILE__": [%s] target latency is %0.0f usec.\n", u->sink->name, (float) target_latency); - - base_rate = u->sink->sample_spec.rate; - - for (o = u->outputs; o; o = o->next) { - uint32_t r = base_rate; - - if (o->total_latency < target_latency) - r -= (uint32_t) (((((double) target_latency - o->total_latency))/u->adjust_time)*r/ 1000000); - else if (o->total_latency > target_latency) - r += (uint32_t) (((((double) o->total_latency - target_latency))/u->adjust_time)*r/ 1000000); - - if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1)) - pa_log_warn(__FILE__": [%s] sample rates too different, not adjusting (%u vs. %u).\n", o->sink_input->name, base_rate, r); - else { - pa_log_info(__FILE__": [%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.\n", o->sink_input->name, r, (double) r / base_rate, (float) o->total_latency); - pa_sink_input_set_rate(o->sink_input, r); - } - } -} - -static void request_memblock(struct userdata *u) { - pa_memchunk chunk; - struct output *o; - assert(u && u->sink); - - update_usage(u); - - if (pa_sink_render(u->sink, RENDER_SIZE, &chunk) < 0) - return; - - for (o = u->outputs; o; o = o->next) - pa_memblockq_push_align(o->memblockq, &chunk, 0); - - pa_memblock_unref(chunk.memblock); -} - -static void time_callback(pa_mainloop_api*a, pa_time_event* e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { - struct userdata *u = userdata; - struct timeval n; - assert(u && a && u->time_event == e); - - adjust_rates(u); - - pa_gettimeofday(&n); - n.tv_sec += u->adjust_time; - u->sink->core->mainloop->time_restart(e, &n); -} - -static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) { - struct output *o = i->userdata; - assert(i && o && o->sink_input && chunk); - - if (pa_memblockq_peek(o->memblockq, chunk) >= 0) - return 0; - - /* Try harder */ - request_memblock(o->userdata); - - return pa_memblockq_peek(o->memblockq, chunk); -} - -static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { - struct output *o = i->userdata; - assert(i && o && o->sink_input && chunk && length); - - pa_memblockq_drop(o->memblockq, chunk, length); - o->counter += length; -} - -static void sink_input_kill_cb(pa_sink_input *i) { - struct output *o = i->userdata; - assert(i && o && o->sink_input); - pa_module_unload_request(o->userdata->module); - clear_up(o->userdata); -} - -static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) { - struct output *o = i->userdata; - assert(i && o && o->sink_input); - - return pa_bytes_to_usec(pa_memblockq_get_length(o->memblockq), &i->sample_spec); -} - -static pa_usec_t sink_get_latency_cb(pa_sink *s) { - struct userdata *u = s->userdata; - assert(s && u && u->sink && u->master); - - return pa_sink_input_get_latency(u->master->sink_input); -} - -static struct output *output_new(struct userdata *u, pa_sink *sink, int resample_method) { - struct output *o = NULL; - char t[256]; - assert(u && sink && u->sink); - - o = pa_xmalloc(sizeof(struct output)); - o->userdata = u; - - o->counter = 0; - o->memblockq = pa_memblockq_new(MEMBLOCKQ_MAXLENGTH, MEMBLOCKQ_MAXLENGTH, pa_frame_size(&u->sink->sample_spec), 0, 0, sink->core->memblock_stat); - - snprintf(t, sizeof(t), "%s: output #%u", u->sink->name, u->n_outputs+1); - if (!(o->sink_input = pa_sink_input_new(sink, __FILE__, t, &u->sink->sample_spec, &u->sink->channel_map, 1, resample_method))) - goto fail; - - o->sink_input->get_latency = sink_input_get_latency_cb; - o->sink_input->peek = sink_input_peek_cb; - o->sink_input->drop = sink_input_drop_cb; - o->sink_input->kill = sink_input_kill_cb; - o->sink_input->userdata = o; - o->sink_input->owner = u->module; - - PA_LLIST_PREPEND(struct output, u->outputs, o); - u->n_outputs++; - return o; - -fail: - - if (o) { - if (o->sink_input) { - pa_sink_input_disconnect(o->sink_input); - pa_sink_input_unref(o->sink_input); - } - - if (o->memblockq) - pa_memblockq_free(o->memblockq); - - pa_xfree(o); - } - - return NULL; -} - -static void output_free(struct output *o) { - assert(o); - PA_LLIST_REMOVE(struct output, o->userdata->outputs, o); - o->userdata->n_outputs--; - pa_memblockq_free(o->memblockq); - pa_sink_input_disconnect(o->sink_input); - pa_sink_input_unref(o->sink_input); - pa_xfree(o); -} - -static void clear_up(struct userdata *u) { - struct output *o; - assert(u); - - if (u->time_event) { - u->core->mainloop->time_free(u->time_event); - u->time_event = NULL; - } - - while ((o = u->outputs)) - output_free(o); - - u->master = NULL; - - if (u->sink) { - pa_sink_disconnect(u->sink); - pa_sink_unref(u->sink); - u->sink = NULL; - } -} - -int pa__init(pa_core *c, pa_module*m) { - struct userdata *u; - pa_modargs *ma = NULL; - const char *master_name, *slaves, *rm; - pa_sink *master_sink; - char *n = NULL; - const char*split_state; - struct timeval tv; - int resample_method = -1; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": failed to parse module arguments\n"); - goto fail; - } - - if ((rm = pa_modargs_get_value(ma, "resample_method", NULL))) { - if ((resample_method = pa_parse_resample_method(rm)) < 0) { - pa_log(__FILE__": invalid resample method '%s'\n", rm); - goto fail; - } - } - - u = pa_xmalloc(sizeof(struct userdata)); - m->userdata = u; - u->sink = NULL; - u->n_outputs = 0; - u->master = NULL; - u->module = m; - u->core = c; - u->time_event = NULL; - u->adjust_time = DEFAULT_ADJUST_TIME; - PA_LLIST_HEAD_INIT(struct output, u->outputs); - - if (pa_modargs_get_value_u32(ma, "adjust_time", &u->adjust_time) < 0) { - pa_log(__FILE__": failed to parse adjust_time value\n"); - goto fail; - } - - if (!(master_name = pa_modargs_get_value(ma, "master", NULL)) || !(slaves = pa_modargs_get_value(ma, "slaves", NULL))) { - pa_log(__FILE__": no master or slave sinks specified\n"); - goto fail; - } - - if (!(master_sink = pa_namereg_get(c, master_name, PA_NAMEREG_SINK, 1))) { - pa_log(__FILE__": invalid master sink '%s'\n", master_name); - goto fail; - } - - if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &master_sink->sample_spec, &master_sink->channel_map))) { - pa_log(__FILE__": failed to create sink\n"); - goto fail; - } - - pa_sink_set_owner(u->sink, m); - u->sink->description = pa_sprintf_malloc("Combined sink"); - u->sink->get_latency = sink_get_latency_cb; - u->sink->userdata = u; - - if (!(u->master = output_new(u, master_sink, resample_method))) { - pa_log(__FILE__": failed to create master sink input on sink '%s'.\n", u->sink->name); - goto fail; - } - - split_state = NULL; - while ((n = pa_split(slaves, ",", &split_state))) { - pa_sink *slave_sink; - - if (!(slave_sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) { - pa_log(__FILE__": invalid slave sink '%s'\n", n); - goto fail; - } - - pa_xfree(n); - - if (!output_new(u, slave_sink, resample_method)) { - pa_log(__FILE__": failed to create slave sink input on sink '%s'.\n", slave_sink->name); - goto fail; - } - } - - if (u->n_outputs <= 1) - pa_log_warn(__FILE__": WARNING: no slave sinks specified.\n"); - - if (u->adjust_time > 0) { - pa_gettimeofday(&tv); - tv.tv_sec += u->adjust_time; - u->time_event = c->mainloop->time_new(c->mainloop, &tv, time_callback, u); - } - - pa_modargs_free(ma); - return 0; - -fail: - pa_xfree(n); - - if (ma) - pa_modargs_free(ma); - - pa__done(c, m); - return -1; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata *u; - assert(c && m); - - if (!(u = m->userdata)) - return; - - clear_up(u); - pa_xfree(u); -} - - diff --git a/polyp/module-defs.h.m4 b/polyp/module-defs.h.m4 deleted file mode 100644 index 2eff25a7..00000000 --- a/polyp/module-defs.h.m4 +++ /dev/null @@ -1,29 +0,0 @@ -dnl $Id$ -changecom(`/*', `*/')dnl -define(`module_name', patsubst(patsubst(fname, `-symdef.h$'), `[^0-9a-zA-Z]', `_'))dnl -define(`c_symbol', patsubst(module_name, `[^0-9a-zA-Z]', `_'))dnl -define(`c_macro', patsubst(module_name, `[^0-9a-zA-Z]', `'))dnl -define(`incmacro', `foo'c_macro`symdeffoo')dnl -define(`gen_symbol', `#define $1 'module_name`_LTX_$1')dnl -#ifndef incmacro -#define incmacro - -#include "core.h" -#include "module.h" - -gen_symbol(pa__init) -gen_symbol(pa__done) -gen_symbol(pa__get_author) -gen_symbol(pa__get_description) -gen_symbol(pa__get_usage) -gen_symbol(pa__get_version) - -int pa__init(struct pa_core *c, struct pa_module*m); -void pa__done(struct pa_core *c, struct pa_module*m); - -const char* pa__get_author(void); -const char* pa__get_description(void); -const char* pa__get_usage(void); -const char* pa__get_version(void); - -#endif diff --git a/polyp/module-detect.c b/polyp/module-detect.c deleted file mode 100644 index ee75da36..00000000 --- a/polyp/module-detect.c +++ /dev/null @@ -1,226 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "module.h" -#include "log.h" -#include "module-detect-symdef.h" -#include "xmalloc.h" -#include "modargs.h" - -PA_MODULE_AUTHOR("Lennart Poettering") -PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers") -PA_MODULE_VERSION(PACKAGE_VERSION) -PA_MODULE_USAGE("just-one=") - -static const char *endswith(const char *haystack, const char *needle) { - size_t l, m; - const char *p; - - if ((l = strlen(haystack)) < (m = strlen(needle))) - return NULL; - - if (strcmp(p = haystack + l - m, needle)) - return NULL; - - return p; -} - -#ifdef HAVE_ALSA -static int detect_alsa(pa_core *c, int just_one) { - FILE *f; - int n = 0, n_sink = 0, n_source = 0; - - if (!(f = fopen("/proc/asound/devices", "r"))) { - - if (errno != ENOENT) - pa_log_error(__FILE__": open(\"/proc/asound/devices\") failed: %s\n", strerror(errno)); - - return -1; - } - - while (!feof(f)) { - char line[64], args[64]; - unsigned device, subdevice; - int is_sink; - - if (!fgets(line, sizeof(line), f)) - break; - - line[strcspn(line, "\r\n")] = 0; - - if (endswith(line, "digital audio playback")) - is_sink = 1; - else if (endswith(line, "digital audio capture")) - is_sink = 0; - else - continue; - - if (just_one && is_sink && n_sink >= 1) - continue; - - if (just_one && !is_sink && n_source >= 1) - continue; - - if (sscanf(line, " %*i: [%u- %u]: ", &device, &subdevice) != 2) - continue; - - /* Only one sink per device */ - if (subdevice != 0) - continue; - - snprintf(args, sizeof(args), "device=hw:%u,0", device); - if (!pa_module_load(c, is_sink ? "module-alsa-sink" : "module-alsa-source", args)) - continue; - - n++; - - if (is_sink) - n_sink++; - else - n_source++; - } - - fclose(f); - - return n; -} -#endif - -#ifdef HAVE_OSS -static int detect_oss(pa_core *c, int just_one) { - FILE *f; - int n = 0, b = 0; - - if (!(f = fopen("/dev/sndstat", "r")) && - !(f = fopen("/proc/sndstat", "r")) && - !(f = fopen("/proc/asound/oss/sndstat", "r"))) { - - if (errno != ENOENT) - pa_log_error(__FILE__": failed to open OSS sndstat device: %s\n", strerror(errno)); - - return -1; - } - - while (!feof(f)) { - char line[64], args[64]; - unsigned device; - - if (!fgets(line, sizeof(line), f)) - break; - - line[strcspn(line, "\r\n")] = 0; - - if (!b) { - b = strcmp(line, "Audio devices:") == 0; - continue; - } - - if (line[0] == 0) - break; - - if (sscanf(line, "%u: ", &device) != 1) - continue; - - if (device == 0) - snprintf(args, sizeof(args), "device=/dev/dsp"); - else - snprintf(args, sizeof(args), "device=/dev/dsp%u", device); - - if (!pa_module_load(c, "module-oss", args)) - continue; - - n++; - - if (just_one) - break; - } - - fclose(f); - return n; -} -#endif - -int pa__init(pa_core *c, pa_module*m) { - int just_one = 0, n = 0; - pa_modargs *ma; - - static const char* const valid_modargs[] = { - "just-one", - NULL - }; - - assert(c); - assert(m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": Failed to parse module arguments\n"); - goto fail; - } - - if (pa_modargs_get_value_boolean(ma, "just-one", &just_one) < 0) { - pa_log(__FILE__": just_one= expects a boolean argument.\n"); - goto fail; - } - -#if HAVE_ALSA - if ((n = detect_alsa(c, just_one)) <= 0) -#endif -#if HAVE_OSS - if ((n = detect_oss(c, just_one)) <= 0) -#endif - { - pa_log_warn(__FILE__": failed to detect any sound hardware.\n"); - goto fail; - } - - pa_log_info(__FILE__": loaded %i modules.\n", n); - - /* We were successful and can unload ourselves now. */ - pa_module_unload_request(m); - - pa_modargs_free(ma); - - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - return -1; -} - - -void pa__done(pa_core *c, pa_module*m) { - /* NOP */ -} - diff --git a/polyp/module-esound-compat-spawnfd.c b/polyp/module-esound-compat-spawnfd.c deleted file mode 100644 index 5051e4d0..00000000 --- a/polyp/module-esound-compat-spawnfd.c +++ /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 -#endif - -#include -#include -#include -#include - -#include "module.h" -#include "util.h" -#include "modargs.h" -#include "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=") -PA_MODULE_VERSION(PACKAGE_VERSION) - -static const char* const valid_modargs[] = { - "fd", - NULL, -}; - -int pa__init(pa_core *c, pa_module*m) { - pa_modargs *ma = NULL; - int ret = -1, fd = -1; - char x = 1; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs)) || - pa_modargs_get_value_s32(ma, "fd", &fd) < 0 || - fd < 0) { - pa_log(__FILE__": Failed to parse module arguments\n"); - goto finish; - } - - if (pa_loop_write(fd, &x, sizeof(x)) != sizeof(x)) - pa_log(__FILE__": WARNING: write(%u, 1, 1) failed: %s\n", fd, strerror(errno)); - - close(fd); - - pa_module_unload_request(m); - - ret = 0; - -finish: - if (ma) - pa_modargs_free(ma); - - return ret; -} - -void pa__done(pa_core *c, pa_module*m) { - assert(c && m); -} - - diff --git a/polyp/module-esound-compat-spawnpid.c b/polyp/module-esound-compat-spawnpid.c deleted file mode 100644 index aa8b0f82..00000000 --- a/polyp/module-esound-compat-spawnpid.c +++ /dev/null @@ -1,78 +0,0 @@ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include - -#include "module.h" -#include "util.h" -#include "modargs.h" -#include "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=") -PA_MODULE_VERSION(PACKAGE_VERSION) - -static const char* const valid_modargs[] = { - "pid", - NULL, -}; - -int pa__init(pa_core *c, pa_module*m) { - pa_modargs *ma = NULL; - int ret = -1; - uint32_t pid = 0; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs)) || - pa_modargs_get_value_u32(ma, "pid", &pid) < 0 || - !pid) { - pa_log(__FILE__": Failed to parse module arguments\n"); - goto finish; - } - - if (kill(pid, SIGUSR1) < 0) - pa_log(__FILE__": WARNING: kill(%u) failed: %s\n", pid, strerror(errno)); - - pa_module_unload_request(m); - - ret = 0; - -finish: - if (ma) - pa_modargs_free(ma); - - return ret; -} - -void pa__done(pa_core *c, pa_module*m) { - assert(c && m); -} - - diff --git a/polyp/module-esound-sink.c b/polyp/module-esound-sink.c deleted file mode 100644 index afd5feed..00000000 --- a/polyp/module-esound-sink.c +++ /dev/null @@ -1,426 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 Sink") -PA_MODULE_VERSION(PACKAGE_VERSION) -PA_MODULE_USAGE("sink_name= server=
cookie= format= channels= rate=") - -#define DEFAULT_SINK_NAME "esound_output" - -struct userdata { - pa_core *core; - - pa_sink *sink; - pa_iochannel *io; - pa_socket_client *client; - - pa_defer_event *defer_event; - - pa_memchunk memchunk; - pa_module *module; - - void *write_data; - size_t write_length, write_index; - - void *read_data; - size_t read_length, read_index; - - enum { STATE_AUTH, STATE_LATENCY, STATE_RUNNING, STATE_DEAD } state; - - pa_usec_t latency; - - esd_format_t format; - int32_t rate; -}; - -static const char* const valid_modargs[] = { - "server", - "cookie", - "rate", - "format", - "channels", - "sink_name", - NULL -}; - -static void cancel(struct userdata *u) { - assert(u); - - u->state = STATE_DEAD; - - if (u->io) { - pa_iochannel_free(u->io); - u->io = NULL; - } - - if (u->defer_event) { - u->core->mainloop->defer_free(u->defer_event); - u->defer_event = NULL; - } - - if (u->sink) { - pa_sink_disconnect(u->sink); - pa_sink_unref(u->sink); - u->sink = NULL; - } - - if (u->module) { - pa_module_unload_request(u->module); - u->module = NULL; - } -} - -static int do_write(struct userdata *u) { - ssize_t r; - assert(u); - - if (!pa_iochannel_is_writable(u->io)) - return 0; - - if (u->write_data) { - assert(u->write_index < u->write_length); - - if ((r = pa_iochannel_write(u->io, (uint8_t*) u->write_data + u->write_index, u->write_length - u->write_index)) <= 0) { - pa_log(__FILE__": write() failed: %s\n", strerror(errno)); - return -1; - } - - u->write_index += r; - assert(u->write_index <= u->write_length); - - if (u->write_index == u->write_length) { - free(u->write_data); - u->write_data = NULL; - u->write_index = u->write_length = 0; - } - } else if (u->state == STATE_RUNNING) { - pa_module_set_used(u->module, pa_idxset_size(u->sink->inputs) + pa_idxset_size(u->sink->monitor_source->outputs)); - - if (!u->memchunk.length) - if (pa_sink_render(u->sink, 8192, &u->memchunk) < 0) - return 0; - - assert(u->memchunk.memblock && u->memchunk.length); - - if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) { - pa_log(__FILE__": write() failed: %s\n", strerror(errno)); - return -1; - } - - u->memchunk.index += r; - u->memchunk.length -= r; - - if (u->memchunk.length <= 0) { - pa_memblock_unref(u->memchunk.memblock); - u->memchunk.memblock = NULL; - } - } - - return 0; -} - -static int handle_response(struct userdata *u) { - assert(u); - - switch (u->state) { - case STATE_AUTH: - assert(u->read_length == sizeof(int32_t)); - - /* Process auth data */ - if (!*(int32_t*) u->read_data) { - pa_log(__FILE__": Authentication failed: %s\n", strerror(errno)); - return -1; - } - - /* Request latency data */ - assert(!u->write_data); - *(int32_t*) (u->write_data = pa_xmalloc(u->write_length = sizeof(int32_t))) = ESD_PROTO_LATENCY; - - u->write_index = 0; - u->state = STATE_LATENCY; - - /* Space for next response */ - assert(u->read_length >= sizeof(int32_t)); - u->read_index = 0; - u->read_length = sizeof(int32_t); - - break; - - case STATE_LATENCY: { - int32_t *p; - assert(u->read_length == sizeof(int32_t)); - - /* Process latency info */ - u->latency = (pa_usec_t) ((double) (*(int32_t*) u->read_data) * 1000000 / 44100); - if (u->latency > 10000000) { - pa_log(__FILE__": WARNING! Invalid latency information received from server\n"); - u->latency = 0; - } - - /* Create stream */ - assert(!u->write_data); - p = u->write_data = pa_xmalloc0(u->write_length = sizeof(int32_t)*3+ESD_NAME_MAX); - *(p++) = ESD_PROTO_STREAM_PLAY; - *(p++) = u->format; - *(p++) = u->rate; - pa_strlcpy((char*) p, "Polypaudio Tunnel", ESD_NAME_MAX); - - u->write_index = 0; - u->state = STATE_RUNNING; - - /* Don't read any further */ - pa_xfree(u->read_data); - u->read_data = NULL; - u->read_index = u->read_length = 0; - - break; - } - - default: - abort(); - } - - return 0; -} - -static int do_read(struct userdata *u) { - assert(u); - - if (!pa_iochannel_is_readable(u->io)) - return 0; - - if (u->state == STATE_AUTH || u->state == STATE_LATENCY) { - ssize_t r; - - if (!u->read_data) - return 0; - - assert(u->read_index < u->read_length); - - if ((r = pa_iochannel_read(u->io, (uint8_t*) u->read_data + u->read_index, u->read_length - u->read_index)) <= 0) { - pa_log(__FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF"); - cancel(u); - return -1; - } - - u->read_index += r; - assert(u->read_index <= u->read_length); - - if (u->read_index == u->read_length) - return handle_response(u); - } - - return 0; -} - -static void do_work(struct userdata *u) { - assert(u); - - u->core->mainloop->defer_enable(u->defer_event, 0); - - if (do_read(u) < 0 || do_write(u) < 0) - cancel(u); -} - -static void notify_cb(pa_sink*s) { - struct userdata *u = s->userdata; - assert(s && u); - - if (pa_iochannel_is_writable(u->io)) - u->core->mainloop->defer_enable(u->defer_event, 1); -} - -static pa_usec_t get_latency_cb(pa_sink *s) { - struct userdata *u = s->userdata; - assert(s && u); - - return - u->latency + - (u->memchunk.memblock ? pa_bytes_to_usec(u->memchunk.length, &s->sample_spec) : 0); -} - -static void defer_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event*e, void *userdata) { - struct userdata *u = userdata; - assert(u); - do_work(u); -} - -static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { - struct userdata *u = userdata; - assert(u); - do_work(u); -} - -static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, void *userdata) { - struct userdata *u = userdata; - - pa_socket_client_unref(u->client); - u->client = NULL; - - if (!io) { - pa_log(__FILE__": connection failed: %s\n", strerror(errno)); - cancel(u); - return; - } - - u->io = io; - pa_iochannel_set_callback(u->io, io_callback, u); -} - -int pa__init(pa_core *c, pa_module*m) { - struct userdata *u = NULL; - const char *p; - pa_sample_spec ss; - pa_modargs *ma = NULL; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": failed to parse module arguments\n"); - goto fail; - } - - ss = c->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &ss) < 0) { - pa_log(__FILE__": invalid sample format specification\n"); - goto fail; - } - - if ((ss.format != PA_SAMPLE_U8 && ss.format != PA_SAMPLE_S16NE) || - (ss.channels > 2)) { - pa_log(__FILE__": esound sample type support is limited to mono/stereo and U8 or S16NE sample data\n"); - goto fail; - } - - u = pa_xmalloc0(sizeof(struct userdata)); - u->core = c; - u->module = m; - m->userdata = u; - u->format = - (ss.format == PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16) | - (ss.channels == 2 ? ESD_STEREO : ESD_MONO); - u->rate = ss.rate; - u->sink = NULL; - u->client = NULL; - u->io = NULL; - u->read_data = u->write_data = NULL; - u->read_index = u->write_index = u->read_length = u->write_length = 0; - u->state = STATE_AUTH; - u->latency = 0; - - if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) { - pa_log(__FILE__": failed to create sink.\n"); - goto fail; - } - - if (!(u->client = pa_socket_client_new_string(u->core->mainloop, p = pa_modargs_get_value(ma, "server", ESD_UNIX_SOCKET_NAME), ESD_DEFAULT_PORT))) { - pa_log(__FILE__": failed to connect to server.\n"); - goto fail; - } - pa_socket_client_set_callback(u->client, on_connection, u); - - /* Prepare the initial request */ - u->write_data = pa_xmalloc(u->write_length = ESD_KEY_LEN + sizeof(int32_t)); - if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", ".esd_auth"), u->write_data, ESD_KEY_LEN) < 0) { - pa_log(__FILE__": failed to load cookie\n"); - goto fail; - } - *(int32_t*) ((uint8_t*) u->write_data + ESD_KEY_LEN) = ESD_ENDIAN_KEY; - - /* Reserve space for the response */ - u->read_data = pa_xmalloc(u->read_length = sizeof(int32_t)); - - u->sink->notify = notify_cb; - u->sink->get_latency = get_latency_cb; - u->sink->userdata = u; - pa_sink_set_owner(u->sink, m); - u->sink->description = pa_sprintf_malloc("Esound sink '%s'", p); - - u->memchunk.memblock = NULL; - u->memchunk.length = 0; - - u->defer_event = c->mainloop->defer_new(c->mainloop, defer_callback, u); - c->mainloop->defer_enable(u->defer_event, 0); - - - pa_modargs_free(ma); - - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - pa__done(c, m); - - return -1; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata *u; - assert(c && m); - - if (!(u = m->userdata)) - return; - - u->module = NULL; - cancel(u); - - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); - - if (u->client) - pa_socket_client_unref(u->client); - - pa_xfree(u->read_data); - pa_xfree(u->write_data); - - pa_xfree(u); -} - - - diff --git a/polyp/module-lirc.c b/polyp/module-lirc.c deleted file mode 100644 index d2e248aa..00000000 --- a/polyp/module-lirc.c +++ /dev/null @@ -1,240 +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 -#endif - -#include -#include -#include -#include -#include -#include - -#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= sink= appname=") - -static const char* const valid_modargs[] = { - "config", - "sink", - "appname", - NULL, -}; - -struct userdata { - int lirc_fd; - pa_io_event *io; - struct lirc_config *config; - char *sink_name; - pa_module *module; - float mute_toggle_save; -}; - -static int lirc_in_use = 0; - -static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void*userdata) { - struct userdata *u = userdata; - char *name = NULL, *code = NULL; - assert(io); - assert(u); - - if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) { - pa_log(__FILE__": lost connection to LIRC daemon.\n"); - goto fail; - } - - if (events & PA_IO_EVENT_INPUT) { - char *c; - - if (lirc_nextcode(&code) != 0 || !code) { - pa_log(__FILE__": lirc_nextcode() failed.\n"); - goto fail; - } - - c = pa_xstrdup(code); - c[strcspn(c, "\n\r")] = 0; - pa_log_debug(__FILE__": raw IR code '%s'\n", c); - pa_xfree(c); - - while (lirc_code2char(u->config, code, &name) == 0 && name) { - enum { INVALID, UP, DOWN, MUTE, RESET, MUTE_TOGGLE } volchange = INVALID; - - pa_log_info(__FILE__": translated IR code '%s'\n", name); - - if (strcasecmp(name, "volume-up") == 0) - volchange = UP; - else if (strcasecmp(name, "volume-down") == 0) - volchange = DOWN; - else if (strcasecmp(name, "mute") == 0) - volchange = MUTE; - else if (strcasecmp(name, "mute-toggle") == 0) - volchange = MUTE_TOGGLE; - else if (strcasecmp(name, "reset") == 0) - volchange = RESET; - - if (volchange == INVALID) - pa_log_warn(__FILE__": recieved unknown IR code '%s'\n", name); - else { - pa_sink *s; - - if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1))) - pa_log(__FILE__": failed to get sink '%s'\n", u->sink_name); - else { - pa_volume_t v = pa_cvolume_avg(pa_sink_get_volume(s, PA_MIXER_HARDWARE)); - pa_cvolume cv; -#define DELTA (PA_VOLUME_NORM/20) - - switch (volchange) { - case UP: - v += PA_VOLUME_NORM/20; - break; - - case DOWN: - if (v > DELTA) - v -= DELTA; - else - v = PA_VOLUME_MUTED; - - break; - - case MUTE: - v = PA_VOLUME_MUTED; - break; - - case RESET: - v = PA_VOLUME_NORM; - break; - - case MUTE_TOGGLE: { - - if (v > 0) { - u->mute_toggle_save = v; - v = PA_VOLUME_MUTED; - } else - v = u->mute_toggle_save; - } - default: - ; - } - - pa_cvolume_set(&cv, PA_CHANNELS_MAX, v); - pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv); - } - } - } - } - - free(code); - - return; - -fail: - u->module->core->mainloop->io_free(u->io); - u->io = NULL; - - pa_module_unload_request(u->module); - - free(code); -} - -int pa__init(pa_core *c, pa_module*m) { - pa_modargs *ma = NULL; - struct userdata *u; - assert(c && m); - - if (lirc_in_use) { - pa_log(__FILE__": module-lirc may no be loaded twice.\n"); - return -1; - } - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": Failed to parse module arguments\n"); - goto fail; - } - - m->userdata = u = pa_xmalloc(sizeof(struct userdata)); - u->module = m; - u->io = NULL; - u->config = NULL; - u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); - u->lirc_fd = -1; - u->mute_toggle_save = 0; - - if ((u->lirc_fd = lirc_init((char*) pa_modargs_get_value(ma, "appname", "polypaudio"), 1)) < 0) { - pa_log(__FILE__": lirc_init() failed.\n"); - goto fail; - } - - if (lirc_readconfig((char*) pa_modargs_get_value(ma, "config", NULL), &u->config, NULL) < 0) { - pa_log(__FILE__": lirc_readconfig() failed.\n"); - goto fail; - } - - u->io = c->mainloop->io_new(c->mainloop, u->lirc_fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u); - - lirc_in_use = 1; - - pa_modargs_free(ma); - - return 0; - -fail: - - if (ma) - pa_modargs_free(ma); - - pa__done(c, m); - return -1; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata *u; - assert(c); - assert(m); - - if (!(u = m->userdata)) - return; - - if (u->io) - m->core->mainloop->io_free(u->io); - - if (u->config) - lirc_freeconfig(u->config); - - if (u->lirc_fd >= 0) - lirc_deinit(); - - pa_xfree(u->sink_name); - pa_xfree(u); - - lirc_in_use = 0; -} diff --git a/polyp/module-match.c b/polyp/module-match.c deleted file mode 100644 index e3e7b17d..00000000 --- a/polyp/module-match.c +++ /dev/null @@ -1,234 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "module.h" -#include "util.h" -#include "modargs.h" -#include "log.h" -#include "subscribe.h" -#include "xmalloc.h" -#include "sink-input.h" -#include "module-match-symdef.h" - -PA_MODULE_AUTHOR("Lennart Poettering") -PA_MODULE_DESCRIPTION("Sink input matching module") -PA_MODULE_USAGE("table=") -PA_MODULE_VERSION(PACKAGE_VERSION) - -#define WHITESPACE "\n\r \t" - -#ifndef DEFAULT_CONFIG_DIR -#define DEFAULT_CONFIG_DIR "/etc/polypaudio" -#endif - -#define DEFAULT_MATCH_TABLE_FILE DEFAULT_CONFIG_DIR"/match.table" -#define DEFAULT_MATCH_TABLE_FILE_USER ".polypaudio/match.table" - -static const char* const valid_modargs[] = { - "table", - NULL, -}; - -struct rule { - regex_t regex; - pa_volume_t volume; - struct rule *next; -}; - -struct userdata { - struct rule *rules; - pa_subscription *subscription; -}; - -static int load_rules(struct userdata *u, const char *filename) { - FILE *f; - int n = 0; - int ret = -1; - struct rule *end = NULL; - char *fn = NULL; - - f = filename ? - fopen(fn = pa_xstrdup(filename), "r") : - pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn); - - if (!f) { - pa_log(__FILE__": failed to open file '%s': %s\n", fn, strerror(errno)); - goto finish; - } - - while (!feof(f)) { - char *d, *v; - pa_volume_t volume; - uint32_t k; - regex_t regex; - char ln[256]; - struct rule *rule; - - if (!fgets(ln, sizeof(ln), f)) - break; - - n++; - - pa_strip_nl(ln); - - if (ln[0] == '#' || !*ln ) - continue; - - d = ln+strcspn(ln, WHITESPACE); - v = d+strspn(d, WHITESPACE); - - - if (!*v) { - pa_log(__FILE__ ": [%s:%u] failed to parse line - too few words\n", filename, n); - goto finish; - } - - *d = 0; - if (pa_atou(v, &k) < 0) { - pa_log(__FILE__": [%s:%u] failed to parse volume\n", filename, n); - goto finish; - } - - volume = (pa_volume_t) k; - - - if (regcomp(®ex, ln, REG_EXTENDED|REG_NOSUB) != 0) { - pa_log(__FILE__": [%s:%u] invalid regular expression\n", filename, n); - goto finish; - } - - rule = pa_xmalloc(sizeof(struct rule)); - rule->regex = regex; - rule->volume = volume; - rule->next = NULL; - - if (end) - end->next = rule; - else - u->rules = rule; - end = rule; - - *d = 0; - } - - ret = 0; - -finish: - if (f) - fclose(f); - - if (fn) - pa_xfree(fn); - - return ret; -} - -static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { - struct userdata *u = userdata; - pa_sink_input *si; - struct rule *r; - assert(c && u); - - if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW)) - return; - - if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx))) - return; - - if (!si->name) - return; - - for (r = u->rules; r; r = r->next) { - if (!regexec(&r->regex, si->name, 0, NULL, 0)) { - pa_cvolume cv; - pa_log_debug(__FILE__": changing volume of sink input '%s' to 0x%03x\n", si->name, r->volume); - pa_cvolume_set(&cv, r->volume, si->sample_spec.channels); - pa_sink_input_set_volume(si, &cv); - } - } -} - -int pa__init(pa_core *c, pa_module*m) { - pa_modargs *ma = NULL; - struct userdata *u; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": Failed to parse module arguments\n"); - goto fail; - } - - u = pa_xmalloc(sizeof(struct userdata)); - u->rules = NULL; - u->subscription = NULL; - m->userdata = u; - - if (load_rules(u, pa_modargs_get_value(ma, "table", NULL)) < 0) - goto fail; - - u->subscription = pa_subscription_new(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, callback, u); - - pa_modargs_free(ma); - return 0; - -fail: - pa__done(c, m); - - if (ma) - pa_modargs_free(ma); - return -1; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata* u; - struct rule *r, *n; - assert(c && m); - - if (!(u = m->userdata)) - return; - - if (u->subscription) - pa_subscription_free(u->subscription); - - for (r = u->rules; r; r = n) { - n = r->next; - - regfree(&r->regex); - pa_xfree(r); - } - - pa_xfree(u); -} - - diff --git a/polyp/module-mmkbd-evdev.c b/polyp/module-mmkbd-evdev.c deleted file mode 100644 index ec45ec4b..00000000 --- a/polyp/module-mmkbd-evdev.c +++ /dev/null @@ -1,249 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include - -#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= sink=") - -#define DEFAULT_DEVICE "/dev/input/event0" - -/* - * This isn't defined in older kernel headers and there is no way of - * detecting it. - */ -struct _input_id { - __u16 bustype; - __u16 vendor; - __u16 product; - __u16 version; -}; - -static const char* const valid_modargs[] = { - "device", - "sink", - NULL, -}; - -struct userdata { - int fd; - pa_io_event *io; - char *sink_name; - pa_module *module; - float mute_toggle_save; -}; - -static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void*userdata) { - struct userdata *u = userdata; - assert(io); - assert(u); - - if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) { - pa_log(__FILE__": lost connection to evdev device.\n"); - goto fail; - } - - if (events & PA_IO_EVENT_INPUT) { - struct input_event ev; - - if (pa_loop_read(u->fd, &ev, sizeof(ev)) <= 0) { - pa_log(__FILE__": failed to read from event device: %s\n", strerror(errno)); - goto fail; - } - - if (ev.type == EV_KEY && (ev.value == 1 || ev.value == 2)) { - enum { INVALID, UP, DOWN, MUTE_TOGGLE } volchange = INVALID; - - pa_log_debug(__FILE__": key code=%u, value=%u\n", ev.code, ev.value); - - switch (ev.code) { - case KEY_VOLUMEDOWN: volchange = DOWN; break; - case KEY_VOLUMEUP: volchange = UP; break; - case KEY_MUTE: volchange = MUTE_TOGGLE; break; - } - - if (volchange != INVALID) { - pa_sink *s; - - if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1))) - pa_log(__FILE__": failed to get sink '%s'\n", u->sink_name); - else { - pa_volume_t v = pa_cvolume_avg(pa_sink_get_volume(s, PA_MIXER_HARDWARE)); - pa_cvolume cv; -#define DELTA (PA_VOLUME_NORM/20) - - switch (volchange) { - case UP: - v += DELTA; - break; - - case DOWN: - if (v > DELTA) - v -= DELTA; - else - v = PA_VOLUME_MUTED; - - break; - - case MUTE_TOGGLE: { - - if (v > 0) { - u->mute_toggle_save = v; - v = PA_VOLUME_MUTED; - } else - v = u->mute_toggle_save; - } - default: - ; - } - - pa_cvolume_set(&cv, PA_CHANNELS_MAX, v); - pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv); - } - } - } - } - - return; - -fail: - u->module->core->mainloop->io_free(u->io); - u->io = NULL; - - pa_module_unload_request(u->module); -} - -#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8))) - -int pa__init(pa_core *c, pa_module*m) { - pa_modargs *ma = NULL; - struct userdata *u; - int version; - struct _input_id input_id; - char name[256]; - uint8_t evtype_bitmask[EV_MAX/8 + 1]; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": Failed to parse module arguments\n"); - goto fail; - } - - m->userdata = u = pa_xmalloc(sizeof(struct userdata)); - u->module = m; - u->io = NULL; - u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); - u->fd = -1; - u->mute_toggle_save = 0; - - if ((u->fd = open(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), O_RDONLY)) < 0) { - pa_log(__FILE__": failed to open evdev device: %s\n", strerror(errno)); - goto fail; - } - - if (ioctl(u->fd, EVIOCGVERSION, &version) < 0) { - pa_log(__FILE__": EVIOCGVERSION failed: %s\n", strerror(errno)); - goto fail; - } - - pa_log_info(__FILE__": evdev driver version %i.%i.%i\n", version >> 16, (version >> 8) & 0xff, version & 0xff); - - if(ioctl(u->fd, EVIOCGID, &input_id)) { - pa_log(__FILE__": EVIOCGID failed: %s\n", strerror(errno)); - goto fail; - } - - pa_log_info(__FILE__": evdev vendor 0x%04hx product 0x%04hx version 0x%04hx bustype %u\n", - input_id.vendor, input_id.product, input_id.version, input_id.bustype); - - if(ioctl(u->fd, EVIOCGNAME(sizeof(name)), name) < 0) { - pa_log(__FILE__": EVIOCGNAME failed: %s\n", strerror(errno)); - goto fail; - } - - pa_log_info(__FILE__": evdev device name: %s\n", name); - - memset(evtype_bitmask, 0, sizeof(evtype_bitmask)); - if (ioctl(u->fd, EVIOCGBIT(0, EV_MAX), evtype_bitmask) < 0) { - pa_log(__FILE__": EVIOCGBIT failed: %s\n", strerror(errno)); - goto fail; - } - - if (!test_bit(EV_KEY, evtype_bitmask)) { - pa_log(__FILE__": device has no keys.\n"); - goto fail; - } - - u->io = c->mainloop->io_new(c->mainloop, u->fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u); - - pa_modargs_free(ma); - - return 0; - -fail: - - if (ma) - pa_modargs_free(ma); - - pa__done(c, m); - return -1; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata *u; - assert(c); - assert(m); - - if (!(u = m->userdata)) - return; - - if (u->io) - m->core->mainloop->io_free(u->io); - - if (u->fd >= 0) - close(u->fd); - - pa_xfree(u->sink_name); - pa_xfree(u); -} diff --git a/polyp/module-native-protocol-fd.c b/polyp/module-native-protocol-fd.c deleted file mode 100644 index 7f09ff91..00000000 --- a/polyp/module-native-protocol-fd.c +++ /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 -#endif - -#include -#include -#include - -#include "module.h" -#include "iochannel.h" -#include "modargs.h" -#include "protocol-native.h" -#include "log.h" -#include "module-native-protocol-fd-symdef.h" - -PA_MODULE_AUTHOR("Lennart Poettering") -PA_MODULE_DESCRIPTION("Native protocol autospawn helper") -PA_MODULE_VERSION(PACKAGE_VERSION) - -static const char* const valid_modargs[] = { - "fd", - "public", - "cookie", - NULL, -}; - -int pa__init(pa_core *c, pa_module*m) { - pa_iochannel *io; - pa_modargs *ma; - int fd, r = -1; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": failed to parse module arguments.\n"); - goto finish; - } - - if (pa_modargs_get_value_s32(ma, "fd", &fd) < 0) { - pa_log(__FILE__": invalid file descriptor.\n"); - goto finish; - } - - io = pa_iochannel_new(c->mainloop, fd, fd); - - if (!(m->userdata = pa_protocol_native_new_iochannel(c, io, m, ma))) { - pa_iochannel_free(io); - goto finish; - } - - r = 0; - -finish: - if (ma) - pa_modargs_free(ma); - - return r; -} - -void pa__done(pa_core *c, pa_module*m) { - assert(c && m); - - pa_protocol_native_free(m->userdata); -} diff --git a/polyp/module-null-sink.c b/polyp/module-null-sink.c deleted file mode 100644 index 93abca78..00000000 --- a/polyp/module-null-sink.c +++ /dev/null @@ -1,149 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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= channels= rate= sink_name=") - -#define DEFAULT_SINK_NAME "null" - -struct userdata { - pa_core *core; - pa_module *module; - pa_sink *sink; - pa_time_event *time_event; - size_t block_size; -}; - -static const char* const valid_modargs[] = { - "rate", - "format", - "channels", - "sink_name", - NULL -}; - -static void time_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) { - struct userdata *u = userdata; - pa_memchunk chunk; - struct timeval ntv = *tv; - size_t l; - - assert(u); - - if (pa_sink_render(u->sink, u->block_size, &chunk) >= 0) { - l = chunk.length; - pa_memblock_unref(chunk.memblock); - } else - l = u->block_size; - - pa_timeval_add(&ntv, pa_bytes_to_usec(l, &u->sink->sample_spec)); - m->time_restart(e, &ntv); -} - -int pa__init(pa_core *c, pa_module*m) { - struct userdata *u = NULL; - pa_sample_spec ss; - pa_modargs *ma = NULL; - struct timeval tv; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": failed to parse module arguments.\n"); - goto fail; - } - - ss = c->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &ss) < 0) { - pa_log(__FILE__": invalid sample format specification.\n"); - goto fail; - } - - u = pa_xmalloc0(sizeof(struct userdata)); - u->core = c; - u->module = m; - m->userdata = u; - - if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) { - pa_log(__FILE__": failed to create sink.\n"); - goto fail; - } - - u->sink->userdata = u; - pa_sink_set_owner(u->sink, m); - u->sink->description = pa_sprintf_malloc("NULL sink"); - - pa_gettimeofday(&tv); - u->time_event = c->mainloop->time_new(c->mainloop, &tv, time_callback, u); - - u->block_size = pa_bytes_per_second(&ss) / 10; - - pa_modargs_free(ma); - - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - pa__done(c, m); - - return -1; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata *u; - assert(c && m); - - if (!(u = m->userdata)) - return; - - pa_sink_disconnect(u->sink); - pa_sink_unref(u->sink); - - u->core->mainloop->time_free(u->time_event); - - pa_xfree(u); -} diff --git a/polyp/module-oss-mmap.c b/polyp/module-oss-mmap.c deleted file mode 100644 index f976cf81..00000000 --- a/polyp/module-oss-mmap.c +++ /dev/null @@ -1,425 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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= source_name= device= record= playback= format= channels= rate= fragments= fragment_size=") - -struct userdata { - pa_sink *sink; - pa_source *source; - pa_core *core; - pa_sample_spec sample_spec; - - size_t in_fragment_size, out_fragment_size, in_fragments, out_fragments, out_fill; - - int fd; - - void *in_mmap, *out_mmap; - size_t in_mmap_length, out_mmap_length; - - pa_io_event *io_event; - - pa_memblock **in_memblocks, **out_memblocks; - unsigned out_current, in_current; - pa_module *module; -}; - -static const char* const valid_modargs[] = { - "sink_name", - "source_name", - "device", - "record", - "playback", - "fragments", - "fragment_size", - "format", - "rate", - "channels", - NULL -}; - -#define DEFAULT_SINK_NAME "oss_output" -#define DEFAULT_SOURCE_NAME "oss_input" -#define DEFAULT_DEVICE "/dev/dsp" - -static void update_usage(struct userdata *u) { - pa_module_set_used(u->module, - (u->sink ? pa_idxset_size(u->sink->inputs) : 0) + - (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0) + - (u->source ? pa_idxset_size(u->source->outputs) : 0)); -} - -static void out_fill_memblocks(struct userdata *u, unsigned n) { - assert(u && u->out_memblocks); - - while (n > 0) { - pa_memchunk chunk; - - if (u->out_memblocks[u->out_current]) - pa_memblock_unref_fixed(u->out_memblocks[u->out_current]); - - chunk.memblock = u->out_memblocks[u->out_current] = pa_memblock_new_fixed((uint8_t*)u->out_mmap+u->out_fragment_size*u->out_current, u->out_fragment_size, 1, u->core->memblock_stat); - assert(chunk.memblock); - chunk.length = chunk.memblock->length; - chunk.index = 0; - - pa_sink_render_into_full(u->sink, &chunk); - - u->out_current++; - while (u->out_current >= u->out_fragments) - u->out_current -= u->out_fragments; - - n--; - } -} - -static void do_write(struct userdata *u) { - struct count_info info; - assert(u && u->sink); - - update_usage(u); - - if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) { - pa_log(__FILE__": SNDCTL_DSP_GETOPTR: %s\n", strerror(errno)); - return; - } - - u->out_fill = (u->out_fragment_size * u->out_fragments) - (info.ptr % u->out_fragment_size); - - if (!info.blocks) - return; - - out_fill_memblocks(u, info.blocks); -} - -static void in_post_memblocks(struct userdata *u, unsigned n) { - assert(u && u->in_memblocks); - - while (n > 0) { - pa_memchunk chunk; - - if (!u->in_memblocks[u->in_current]) { - chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed((uint8_t*) u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size, 1, u->core->memblock_stat); - chunk.length = chunk.memblock->length; - chunk.index = 0; - - pa_source_post(u->source, &chunk); - } - - u->in_current++; - while (u->in_current >= u->in_fragments) - u->in_current -= u->in_fragments; - - n--; - } -} - -static void in_clear_memblocks(struct userdata*u, unsigned n) { - unsigned i = u->in_current; - assert(u && u->in_memblocks); - - if (n > u->in_fragments) - n = u->in_fragments; - - while (n > 0) { - if (u->in_memblocks[i]) { - pa_memblock_unref_fixed(u->in_memblocks[i]); - u->in_memblocks[i] = NULL; - } - - i++; - while (i >= u->in_fragments) - i -= u->in_fragments; - - n--; - } -} - -static void do_read(struct userdata *u) { - struct count_info info; - assert(u && u->source); - - update_usage(u); - - if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) { - pa_log(__FILE__": SNDCTL_DSP_GETIPTR: %s\n", strerror(errno)); - return; - } - - if (!info.blocks) - return; - - in_post_memblocks(u, info.blocks); - in_clear_memblocks(u, u->in_fragments/2); -} - -static void io_callback(pa_mainloop_api *m, pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t f, void *userdata) { - struct userdata *u = userdata; - assert (u && u->core->mainloop == m && u->io_event == e); - - if (f & PA_IO_EVENT_INPUT) - do_read(u); - if (f & PA_IO_EVENT_OUTPUT) - do_write(u); -} - -static pa_usec_t sink_get_latency_cb(pa_sink *s) { - struct userdata *u = s->userdata; - assert(s && u); - - do_write(u); - return pa_bytes_to_usec(u->out_fill, &s->sample_spec); -} - -int pa__init(pa_core *c, pa_module*m) { - struct audio_buf_info info; - struct userdata *u = NULL; - const char *p; - int nfrags, frag_size; - int mode, caps; - int enable_bits = 0, zero = 0; - int playback = 1, record = 1; - pa_modargs *ma = NULL; - assert(c && m); - - m->userdata = u = pa_xmalloc0(sizeof(struct userdata)); - u->module = m; - u->fd = -1; - u->core = c; - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": failed to parse module arguments.\n"); - goto fail; - } - - if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) { - pa_log(__FILE__": record= and playback= expect numeric arguments.\n"); - goto fail; - } - - if (!playback && !record) { - pa_log(__FILE__": neither playback nor record enabled for device.\n"); - goto fail; - } - - mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0)); - - nfrags = 12; - frag_size = 1024; - if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) { - pa_log(__FILE__": failed to parse fragments arguments\n"); - goto fail; - } - - u->sample_spec = c->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &u->sample_spec) < 0) { - pa_log(__FILE__": failed to parse sample specification\n"); - goto fail; - } - - if ((u->fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0) - goto fail; - - if (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_REALTIME) || !(caps & DSP_CAP_TRIGGER)) { - pa_log(__FILE__": OSS device not mmap capable.\n"); - goto fail; - } - - pa_log(__FILE__": device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR")); - - if (nfrags >= 2 && frag_size >= 1) - if (pa_oss_set_fragments(u->fd, nfrags, frag_size) < 0) - goto fail; - - if (pa_oss_auto_format(u->fd, &u->sample_spec) < 0) - goto fail; - - if (mode != O_WRONLY) { - if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) { - pa_log(__FILE__": SNDCTL_DSP_GETISPACE: %s\n", strerror(errno)); - goto fail; - } - - pa_log_info(__FILE__": input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); - u->in_mmap_length = (u->in_fragment_size = info.fragsize) * (u->in_fragments = info.fragstotal); - - if ((u->in_mmap = mmap(NULL, u->in_mmap_length, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) { - if (mode == O_RDWR) { - pa_log(__FILE__": mmap failed for input. Changing to O_WRONLY mode.\n"); - mode = O_WRONLY; - } else { - pa_log(__FILE__": mmap(): %s\n", strerror(errno)); - goto fail; - } - } else { - - u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &u->sample_spec, NULL); - assert(u->source); - u->source->userdata = u; - pa_source_set_owner(u->source, m); - u->source->description = pa_sprintf_malloc("Open Sound System PCM/mmap() on '%s'", p); - - u->in_memblocks = pa_xmalloc0(sizeof(pa_memblock *)*u->in_fragments); - - enable_bits |= PCM_ENABLE_INPUT; - } - } - - if (mode != O_RDONLY) { - if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { - pa_log(__FILE__": SNDCTL_DSP_GETOSPACE: %s\n", strerror(errno)); - goto fail; - } - - pa_log_info(__FILE__": output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); - u->out_mmap_length = (u->out_fragment_size = info.fragsize) * (u->out_fragments = info.fragstotal); - - if ((u->out_mmap = mmap(NULL, u->out_mmap_length, PROT_WRITE, MAP_SHARED, u->fd, 0)) == MAP_FAILED) { - if (mode == O_RDWR) { - pa_log(__FILE__": mmap filed for output. Changing to O_RDONLY mode.\n"); - mode = O_RDONLY; - } else { - pa_log(__FILE__": mmap(): %s\n", strerror(errno)); - goto fail; - } - } else { - pa_silence_memory(u->out_mmap, u->out_mmap_length, &u->sample_spec); - - u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &u->sample_spec, NULL); - assert(u->sink); - u->sink->get_latency = sink_get_latency_cb; - u->sink->userdata = u; - pa_sink_set_owner(u->sink, m); - u->sink->description = pa_sprintf_malloc("Open Sound System PCM/mmap() on '%s'", p); - - u->out_memblocks = pa_xmalloc0(sizeof(struct memblock *)*u->out_fragments); - - enable_bits |= PCM_ENABLE_OUTPUT; - } - } - - zero = 0; - if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero) < 0) { - pa_log(__FILE__": SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno)); - goto fail; - } - - if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) { - pa_log(__FILE__": SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno)); - goto fail; - } - - assert(u->source || u->sink); - - u->io_event = c->mainloop->io_new(c->mainloop, u->fd, (u->source ? PA_IO_EVENT_INPUT : 0) | (u->sink ? PA_IO_EVENT_OUTPUT : 0), io_callback, u); - assert(u->io_event); - - pa_modargs_free(ma); - - return 0; - -fail: - pa__done(c, m); - - if (ma) - pa_modargs_free(ma); - - return -1; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata *u; - assert(c && m); - - if (!(u = m->userdata)) - return; - - if (u->out_memblocks) { - unsigned i; - for (i = 0; i < u->out_fragments; i++) - if (u->out_memblocks[i]) - pa_memblock_unref_fixed(u->out_memblocks[i]); - pa_xfree(u->out_memblocks); - } - - if (u->in_memblocks) { - unsigned i; - for (i = 0; i < u->in_fragments; i++) - if (u->in_memblocks[i]) - pa_memblock_unref_fixed(u->in_memblocks[i]); - pa_xfree(u->in_memblocks); - } - - if (u->in_mmap && u->in_mmap != MAP_FAILED) - munmap(u->in_mmap, u->in_mmap_length); - - if (u->out_mmap && u->out_mmap != MAP_FAILED) - munmap(u->out_mmap, u->out_mmap_length); - - if (u->sink) { - pa_sink_disconnect(u->sink); - pa_sink_unref(u->sink); - } - - if (u->source) { - pa_source_disconnect(u->source); - pa_source_unref(u->source); - } - - if (u->io_event) - u->core->mainloop->io_free(u->io_event); - - if (u->fd >= 0) - close(u->fd); - - pa_xfree(u); -} diff --git a/polyp/module-oss.c b/polyp/module-oss.c deleted file mode 100644 index b5c7ae8c..00000000 --- a/polyp/module-oss.c +++ /dev/null @@ -1,467 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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= source_name= device= record= playback= format= channels= rate= fragments= fragment_size=") - -struct userdata { - pa_sink *sink; - pa_source *source; - pa_iochannel *io; - pa_core *core; - - pa_memchunk memchunk, silence; - - uint32_t in_fragment_size, out_fragment_size, sample_size; - int use_getospace, use_getispace; - - int fd; - pa_module *module; -}; - -static const char* const valid_modargs[] = { - "sink_name", - "source_name", - "device", - "record", - "playback", - "fragments", - "fragment_size", - "format", - "rate", - "channels", - NULL -}; - -#define DEFAULT_SINK_NAME "oss_output" -#define DEFAULT_SOURCE_NAME "oss_input" -#define DEFAULT_DEVICE "/dev/dsp" - -static void update_usage(struct userdata *u) { - pa_module_set_used(u->module, - (u->sink ? pa_idxset_size(u->sink->inputs) : 0) + - (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0) + - (u->source ? pa_idxset_size(u->source->outputs) : 0)); -} - -static void do_write(struct userdata *u) { - pa_memchunk *memchunk; - ssize_t r; - size_t l; - int loop = 0; - - assert(u); - - if (!u->sink || !pa_iochannel_is_writable(u->io)) - return; - - update_usage(u); - - l = u->out_fragment_size; - - if (u->use_getospace) { - audio_buf_info info; - - if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) - u->use_getospace = 0; - else { - if (info.bytes/l > 0) { - l = (info.bytes/l)*l; - loop = 1; - } - } - } - - do { - memchunk = &u->memchunk; - - if (!memchunk->length) - if (pa_sink_render(u->sink, l, memchunk) < 0) - memchunk = &u->silence; - - assert(memchunk->memblock); - assert(memchunk->memblock->data); - assert(memchunk->length); - - if ((r = pa_iochannel_write(u->io, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length)) < 0) { - pa_log(__FILE__": write() failed: %s\n", strerror(errno)); - break; - } - - if (memchunk == &u->silence) - assert(r % u->sample_size == 0); - else { - u->memchunk.index += r; - u->memchunk.length -= r; - - if (u->memchunk.length <= 0) { - pa_memblock_unref(u->memchunk.memblock); - u->memchunk.memblock = NULL; - } - } - - l = l > (size_t) r ? l - r : 0; - } while (loop && l > 0); -} - -static void do_read(struct userdata *u) { - pa_memchunk memchunk; - ssize_t r; - size_t l; - int loop = 0; - assert(u); - - if (!u->source || !pa_iochannel_is_readable(u->io) || !pa_idxset_size(u->source->outputs)) - return; - - update_usage(u); - - l = u->in_fragment_size; - - if (u->use_getispace) { - audio_buf_info info; - - if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) - u->use_getispace = 0; - else { - if (info.bytes/l > 0) { - l = (info.bytes/l)*l; - loop = 1; - } - } - } - - do { - memchunk.memblock = pa_memblock_new(l, u->core->memblock_stat); - assert(memchunk.memblock); - if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) { - pa_memblock_unref(memchunk.memblock); - if (errno != EAGAIN) - pa_log(__FILE__": read() failed: %s\n", strerror(errno)); - break; - } - - assert(r <= (ssize_t) memchunk.memblock->length); - memchunk.length = memchunk.memblock->length = r; - memchunk.index = 0; - - pa_source_post(u->source, &memchunk); - pa_memblock_unref(memchunk.memblock); - - l = l > (size_t) r ? l - r : 0; - } while (loop && l > 0); -} - -static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { - struct userdata *u = userdata; - assert(u); - do_write(u); - do_read(u); -} - -static void source_notify_cb(pa_source *s) { - struct userdata *u = s->userdata; - assert(u); - do_read(u); -} - -static pa_usec_t sink_get_latency_cb(pa_sink *s) { - pa_usec_t r = 0; - int arg; - struct userdata *u = s->userdata; - assert(s && u && u->sink); - - if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) { - pa_log_info(__FILE__": device doesn't support SNDCTL_DSP_GETODELAY: %s\n", strerror(errno)); - s->get_latency = NULL; - return 0; - } - - r += pa_bytes_to_usec(arg, &s->sample_spec); - - if (u->memchunk.memblock) - r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec); - - return r; -} - -static pa_usec_t source_get_latency_cb(pa_source *s) { - struct userdata *u = s->userdata; - audio_buf_info info; - assert(s && u && u->source); - - if (!u->use_getispace) - return 0; - - if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) { - u->use_getispace = 0; - return 0; - } - - if (info.bytes <= 0) - return 0; - - return pa_bytes_to_usec(info.bytes, &s->sample_spec); -} - -static int sink_get_hw_volume(pa_sink *s) { - struct userdata *u = s->userdata; - char cv[PA_CVOLUME_SNPRINT_MAX]; - unsigned vol; - - if (ioctl(u->fd, SOUND_MIXER_READ_PCM, &vol) < 0) { - pa_log_info(__FILE__": device doesn't support reading mixer settings: %s\n", strerror(errno)); - s->get_hw_volume = NULL; - return -1; - } - - s->hw_volume.values[0] = ((vol & 0xFF) * PA_VOLUME_NORM) / 100; - - if ((s->hw_volume.channels = s->sample_spec.channels) >= 2) - s->hw_volume.values[1] = (((vol >> 8) & 0xFF) * PA_VOLUME_NORM) / 100; - - pa_log_info(__FILE__": Read mixer settings: %s\n", pa_cvolume_snprint(cv, sizeof(cv), &s->hw_volume)); - return 0; -} - -static int sink_set_hw_volume(pa_sink *s) { - struct userdata *u = s->userdata; - char cv[PA_CVOLUME_SNPRINT_MAX]; - unsigned vol; - - vol = (s->hw_volume.values[0]*100)/PA_VOLUME_NORM; - - if (s->sample_spec.channels >= 2) - vol |= ((s->hw_volume.values[1]*100)/PA_VOLUME_NORM) << 8; - - if (ioctl(u->fd, SOUND_MIXER_WRITE_PCM, &vol) < 0) { - pa_log_info(__FILE__": device doesn't support writing mixer settings: %s\n", strerror(errno)); - s->set_hw_volume = NULL; - return -1; - } - - pa_log_info(__FILE__": Wrote mixer settings: %s\n", pa_cvolume_snprint(cv, sizeof(cv), &s->hw_volume)); - return 0; -} - -int pa__init(pa_core *c, pa_module*m) { - struct audio_buf_info info; - struct userdata *u = NULL; - const char *p; - int fd = -1; - int nfrags, frag_size, in_frag_size, out_frag_size; - int mode; - int record = 1, playback = 1; - pa_sample_spec ss; - pa_modargs *ma = NULL; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": failed to parse module arguments.\n"); - goto fail; - } - - if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) { - pa_log(__FILE__": record= and playback= expect numeric argument.\n"); - goto fail; - } - - if (!playback && !record) { - pa_log(__FILE__": neither playback nor record enabled for device.\n"); - goto fail; - } - - mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0)); - - nfrags = 12; - frag_size = 1024; - if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) { - pa_log(__FILE__": failed to parse fragments arguments\n"); - goto fail; - } - - ss = c->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &ss) < 0) { - pa_log(__FILE__": failed to parse sample specification\n"); - goto fail; - } - - if ((fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, NULL)) < 0) - goto fail; - - pa_log_info(__FILE__": device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR")); - - if (nfrags >= 2 && frag_size >= 1) - if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0) - goto fail; - - if (pa_oss_auto_format(fd, &ss) < 0) - goto fail; - - if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) { - pa_log(__FILE__": SNDCTL_DSP_GETBLKSIZE: %s\n", strerror(errno)); - goto fail; - } - assert(frag_size); - in_frag_size = out_frag_size = frag_size; - - u = pa_xmalloc(sizeof(struct userdata)); - u->core = c; - u->use_getospace = u->use_getispace = 0; - - if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) { - pa_log_info(__FILE__": input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); - in_frag_size = info.fragsize; - u->use_getispace = 1; - } - - if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) { - pa_log_info(__FILE__": output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); - out_frag_size = info.fragsize; - u->use_getospace = 1; - } - - if (mode != O_WRONLY) { - u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL); - assert(u->source); - u->source->userdata = u; - u->source->notify = source_notify_cb; - u->source->get_latency = source_get_latency_cb; - pa_source_set_owner(u->source, m); - u->source->description = pa_sprintf_malloc("Open Sound System PCM on '%s'", p); - } else - u->source = NULL; - - if (mode != O_RDONLY) { - u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL); - assert(u->sink); - u->sink->get_latency = sink_get_latency_cb; - u->sink->get_hw_volume = sink_get_hw_volume; - u->sink->set_hw_volume = sink_set_hw_volume; - u->sink->userdata = u; - pa_sink_set_owner(u->sink, m); - u->sink->description = pa_sprintf_malloc("Open Sound System PCM on '%s'", p); - } else - u->sink = NULL; - - assert(u->source || u->sink); - - u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : -1); - assert(u->io); - pa_iochannel_set_callback(u->io, io_callback, u); - u->fd = fd; - - u->memchunk.memblock = NULL; - u->memchunk.length = 0; - u->sample_size = pa_frame_size(&ss); - - u->out_fragment_size = out_frag_size; - u->in_fragment_size = in_frag_size; - u->silence.memblock = pa_memblock_new(u->silence.length = u->out_fragment_size, u->core->memblock_stat); - assert(u->silence.memblock); - pa_silence_memblock(u->silence.memblock, &ss); - u->silence.index = 0; - - u->module = m; - m->userdata = u; - - pa_modargs_free(ma); - - /* - * Some crappy drivers do not start the recording until we read something. - * Without this snippet, poll will never register the fd as ready. - */ - if (u->source) { - char buf[u->sample_size]; - read(u->fd, buf, u->sample_size); - } - - /* Read mixer settings */ - if (u->sink) - sink_get_hw_volume(u->sink); - - return 0; - -fail: - if (fd >= 0) - close(fd); - - if (ma) - pa_modargs_free(ma); - - return -1; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata *u; - assert(c && m); - - if (!(u = m->userdata)) - return; - - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); - if (u->silence.memblock) - pa_memblock_unref(u->silence.memblock); - - if (u->sink) { - pa_sink_disconnect(u->sink); - pa_sink_unref(u->sink); - } - - if (u->source) { - pa_source_disconnect(u->source); - pa_source_unref(u->source); - } - - pa_iochannel_free(u->io); - pa_xfree(u); -} diff --git a/polyp/module-pipe-sink.c b/polyp/module-pipe-sink.c deleted file mode 100644 index 20e220ce..00000000 --- a/polyp/module-pipe-sink.c +++ /dev/null @@ -1,236 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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= file= format= channels= rate=") - -#define DEFAULT_FIFO_NAME "/tmp/music.output" -#define DEFAULT_SINK_NAME "fifo_output" - -struct userdata { - pa_core *core; - - char *filename; - - pa_sink *sink; - pa_iochannel *io; - pa_defer_event *defer_event; - - pa_memchunk memchunk; - pa_module *module; -}; - -static const char* const valid_modargs[] = { - "file", - "rate", - "format", - "channels", - "sink_name", - NULL -}; - -static void do_write(struct userdata *u) { - ssize_t r; - assert(u); - - u->core->mainloop->defer_enable(u->defer_event, 0); - - if (!pa_iochannel_is_writable(u->io)) - return; - - pa_module_set_used(u->module, pa_idxset_size(u->sink->inputs) + pa_idxset_size(u->sink->monitor_source->outputs)); - - if (!u->memchunk.length) - if (pa_sink_render(u->sink, PIPE_BUF, &u->memchunk) < 0) - return; - - assert(u->memchunk.memblock && u->memchunk.length); - - if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) { - pa_log(__FILE__": write() failed: %s\n", strerror(errno)); - return; - } - - u->memchunk.index += r; - u->memchunk.length -= r; - - if (u->memchunk.length <= 0) { - pa_memblock_unref(u->memchunk.memblock); - u->memchunk.memblock = NULL; - } -} - -static void notify_cb(pa_sink*s) { - struct userdata *u = s->userdata; - assert(s && u); - - if (pa_iochannel_is_writable(u->io)) - u->core->mainloop->defer_enable(u->defer_event, 1); -} - -static pa_usec_t get_latency_cb(pa_sink *s) { - struct userdata *u = s->userdata; - assert(s && u); - - return u->memchunk.memblock ? pa_bytes_to_usec(u->memchunk.length, &s->sample_spec) : 0; -} - -static void defer_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event*e, void *userdata) { - struct userdata *u = userdata; - assert(u); - do_write(u); -} - -static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { - struct userdata *u = userdata; - assert(u); - do_write(u); -} - -int pa__init(pa_core *c, pa_module*m) { - struct userdata *u = NULL; - struct stat st; - const char *p; - int fd = -1; - pa_sample_spec ss; - pa_modargs *ma = NULL; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": failed to parse module arguments\n"); - goto fail; - } - - ss = c->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &ss) < 0) { - pa_log(__FILE__": invalid sample format specification\n"); - goto fail; - } - - mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777); - - if ((fd = open(p, O_RDWR)) < 0) { - pa_log(__FILE__": open('%s'): %s\n", p, strerror(errno)); - goto fail; - } - - pa_fd_set_cloexec(fd, 1); - - if (fstat(fd, &st) < 0) { - pa_log(__FILE__": fstat('%s'): %s\n", p, strerror(errno)); - goto fail; - } - - if (!S_ISFIFO(st.st_mode)) { - pa_log(__FILE__": '%s' is not a FIFO.\n", p); - goto fail; - } - - u = pa_xmalloc0(sizeof(struct userdata)); - u->filename = pa_xstrdup(p); - u->core = c; - u->module = m; - m->userdata = u; - - if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) { - pa_log(__FILE__": failed to create sink.\n"); - goto fail; - } - u->sink->notify = notify_cb; - u->sink->get_latency = get_latency_cb; - u->sink->userdata = u; - pa_sink_set_owner(u->sink, m); - u->sink->description = pa_sprintf_malloc("Unix FIFO sink '%s'", p); - assert(u->sink->description); - - u->io = pa_iochannel_new(c->mainloop, -1, fd); - assert(u->io); - pa_iochannel_set_callback(u->io, io_callback, u); - - u->memchunk.memblock = NULL; - u->memchunk.length = 0; - - u->defer_event = c->mainloop->defer_new(c->mainloop, defer_callback, u); - assert(u->defer_event); - c->mainloop->defer_enable(u->defer_event, 0); - - pa_modargs_free(ma); - - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - if (fd >= 0) - close(fd); - - pa__done(c, m); - - return -1; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata *u; - assert(c && m); - - if (!(u = m->userdata)) - return; - - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); - - pa_sink_disconnect(u->sink); - pa_sink_unref(u->sink); - pa_iochannel_free(u->io); - u->core->mainloop->defer_free(u->defer_event); - - assert(u->filename); - unlink(u->filename); - pa_xfree(u->filename); - - pa_xfree(u); -} diff --git a/polyp/module-pipe-source.c b/polyp/module-pipe-source.c deleted file mode 100644 index 42f13d4b..00000000 --- a/polyp/module-pipe-source.c +++ /dev/null @@ -1,209 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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= file= format= channels= rate=") - -#define DEFAULT_FIFO_NAME "/tmp/music.input" -#define DEFAULT_SOURCE_NAME "fifo_input" - -struct userdata { - pa_core *core; - - char *filename; - - pa_source *source; - pa_iochannel *io; - pa_module *module; - pa_memchunk chunk; -}; - -static const char* const valid_modargs[] = { - "file", - "rate", - "channels", - "format", - "source_name", - NULL -}; - -static void do_read(struct userdata *u) { - ssize_t r; - pa_memchunk chunk; - assert(u); - - if (!pa_iochannel_is_readable(u->io)) - return; - - pa_module_set_used(u->module, pa_idxset_size(u->source->outputs)); - - if (!u->chunk.memblock) { - u->chunk.memblock = pa_memblock_new(1024, u->core->memblock_stat); - u->chunk.index = chunk.length = 0; - } - - assert(u->chunk.memblock && u->chunk.memblock->length > u->chunk.index); - if ((r = pa_iochannel_read(u->io, (uint8_t*) u->chunk.memblock->data + u->chunk.index, u->chunk.memblock->length - u->chunk.index)) <= 0) { - pa_log(__FILE__": read() failed: %s\n", strerror(errno)); - return; - } - - u->chunk.length = r; - pa_source_post(u->source, &u->chunk); - u->chunk.index += r; - - if (u->chunk.index >= u->chunk.memblock->length) { - u->chunk.index = u->chunk.length = 0; - pa_memblock_unref(u->chunk.memblock); - u->chunk.memblock = NULL; - } -} - -static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { - struct userdata *u = userdata; - assert(u); - do_read(u); -} - -int pa__init(pa_core *c, pa_module*m) { - struct userdata *u = NULL; - struct stat st; - const char *p; - int fd = -1; - pa_sample_spec ss; - pa_modargs *ma = NULL; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": failed to parse module arguments\n"); - goto fail; - } - - ss = c->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &ss) < 0) { - pa_log(__FILE__": invalid sample format specification\n"); - goto fail; - } - - mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777); - - if ((fd = open(p, O_RDWR)) < 0) { - pa_log(__FILE__": open('%s'): %s\n", p, strerror(errno)); - goto fail; - } - - pa_fd_set_cloexec(fd, 1); - - if (fstat(fd, &st) < 0) { - pa_log(__FILE__": fstat('%s'): %s\n", p, strerror(errno)); - goto fail; - } - - if (!S_ISFIFO(st.st_mode)) { - pa_log(__FILE__": '%s' is not a FIFO.\n", p); - goto fail; - } - - u = pa_xmalloc0(sizeof(struct userdata)); - - u->filename = pa_xstrdup(p); - u->core = c; - - if (!(u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL))) { - pa_log(__FILE__": failed to create source.\n"); - goto fail; - } - u->source->userdata = u; - pa_source_set_owner(u->source, m); - u->source->description = pa_sprintf_malloc("Unix FIFO source '%s'", p); - assert(u->source->description); - - u->io = pa_iochannel_new(c->mainloop, fd, -1); - assert(u->io); - pa_iochannel_set_callback(u->io, io_callback, u); - - u->chunk.memblock = NULL; - u->chunk.index = u->chunk.length = 0; - - u->module = m; - m->userdata = u; - - pa_modargs_free(ma); - - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - if (fd >= 0) - close(fd); - - pa__done(c, m); - - return -1; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata *u; - assert(c && m); - - if (!(u = m->userdata)) - return; - - if (u->chunk.memblock) - pa_memblock_unref(u->chunk.memblock); - - pa_source_disconnect(u->source); - pa_source_unref(u->source); - pa_iochannel_free(u->io); - - assert(u->filename); - unlink(u->filename); - pa_xfree(u->filename); - - pa_xfree(u); -} diff --git a/polyp/module-protocol-stub.c b/polyp/module-protocol-stub.c deleted file mode 100644 index a0314368..00000000 --- a/polyp/module-protocol-stub.c +++ /dev/null @@ -1,262 +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 -#endif - -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SYS_SOCKET_H -#include -#endif -#ifdef HAVE_ARPA_INET_H -#include -#endif -#ifdef HAVE_NETINET_IN_H -#include -#endif - -#include "winsock.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= loopback=" -#elif defined(USE_TCP6_SOCKETS) -#define SOCKET_DESCRIPTION "(TCP/IPv6 sockets)" -#define SOCKET_USAGE "port= loopback=" -#else -#define SOCKET_DESCRIPTION "(UNIX sockets)" -#define SOCKET_USAGE "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= format= channels= sink= source= playback= 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= cookie= "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= source= public= cookie= "SOCKET_USAGE) -#else - #error "Broken build system" -#endif - -PA_MODULE_AUTHOR("Lennart Poettering") -PA_MODULE_VERSION(PACKAGE_VERSION) - -static const char* const valid_modargs[] = { - MODULE_ARGUMENTS -#if defined(USE_TCP_SOCKETS) || defined(USE_TCP6_SOCKETS) - "port", - "loopback", -#else - "socket", -#endif - NULL -}; - -static pa_socket_server *create_socket_server(pa_core *c, pa_modargs *ma) { - pa_socket_server *s; -#if defined(USE_TCP_SOCKETS) || defined(USE_TCP6_SOCKETS) - int loopback = 1; - uint32_t port = IPV4_PORT; - - if (pa_modargs_get_value_boolean(ma, "loopback", &loopback) < 0) { - pa_log(__FILE__": loopback= expects a boolean argument.\n"); - return NULL; - } - - if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port < 1 || port > 0xFFFF) { - pa_log(__FILE__": port= expects a numerical argument between 1 and 65535.\n"); - return NULL; - } - -#ifdef USE_TCP6_SOCKETS - if (!(s = pa_socket_server_new_ipv6(c->mainloop, loopback ? (const uint8_t*) &in6addr_loopback : (const uint8_t*) &in6addr_any, port))) - return NULL; -#else - if (!(s = pa_socket_server_new_ipv4(c->mainloop, loopback ? INADDR_LOOPBACK : INADDR_ANY, port, TCPWRAP_SERVICE))) - return NULL; -#endif - -#else - int r; - const char *v; - char tmp[PATH_MAX]; - - v = pa_modargs_get_value(ma, "socket", UNIX_SOCKET); - assert(v); - - pa_runtime_path(v, tmp, sizeof(tmp)); - - if (pa_make_secure_parent_dir(tmp) < 0) { - pa_log(__FILE__": Failed to create secure socket directory.\n"); - return NULL; - } - - if ((r = pa_unix_socket_remove_stale(tmp)) < 0) { - pa_log(__FILE__": Failed to remove stale UNIX socket '%s': %s\n", tmp, strerror(errno)); - return NULL; - } - - if (r) - pa_log(__FILE__": Removed stale UNIX socket '%s'.", tmp); - - if (!(s = pa_socket_server_new_unix(c->mainloop, tmp))) - return NULL; - -#endif - return s; -} - -int pa__init(pa_core *c, pa_module*m) { - pa_socket_server *s; - pa_modargs *ma = NULL; - int ret = -1; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": Failed to parse module arguments\n"); - goto finish; - } - - if (!(s = create_socket_server(c, ma))) - goto finish; - - if (!(m->userdata = protocol_new(c, s, m, ma))) { - pa_socket_server_unref(s); - goto finish; - } - - ret = 0; - -finish: - if (ma) - pa_modargs_free(ma); - - return ret; -} - -void pa__done(pa_core *c, pa_module*m) { - assert(c && m); - -#if defined(USE_PROTOCOL_ESOUND) - if (remove (ESD_UNIX_SOCKET_NAME) != 0) - pa_log("%s: Failed to remove %s : %s.\n", __FILE__, ESD_UNIX_SOCKET_NAME, strerror (errno)); - if (remove (ESD_UNIX_SOCKET_DIR) != 0) - pa_log("%s: Failed to remove %s : %s.\n", __FILE__, ESD_UNIX_SOCKET_DIR, strerror (errno)); -#endif - - protocol_free(m->userdata); -} diff --git a/polyp/module-sine.c b/polyp/module-sine.c deleted file mode 100644 index 529c061a..00000000 --- a/polyp/module-sine.c +++ /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 -#endif - -#include -#include -#include - -#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= frequency=") -PA_MODULE_VERSION(PACKAGE_VERSION) - -struct userdata { - pa_core *core; - pa_module *module; - pa_sink_input *sink_input; - pa_memblock *memblock; - size_t peek_index; -}; - -static const char* const valid_modargs[] = { - "sink", - "frequency", - NULL, -}; - -static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) { - struct userdata *u; - assert(i && chunk && i->userdata); - u = i->userdata; - - chunk->memblock = pa_memblock_ref(u->memblock); - chunk->index = u->peek_index; - chunk->length = u->memblock->length - u->peek_index; - return 0; -} - -static void sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { - struct userdata *u; - assert(i && chunk && length && i->userdata); - u = i->userdata; - - assert(chunk->memblock == u->memblock && length <= u->memblock->length-u->peek_index); - - u->peek_index += length; - - if (u->peek_index >= u->memblock->length) - u->peek_index = 0; -} - -static void sink_input_kill(pa_sink_input *i) { - struct userdata *u; - assert(i && i->userdata); - u = i->userdata; - - pa_sink_input_disconnect(u->sink_input); - pa_sink_input_unref(u->sink_input); - u->sink_input = NULL; - - pa_module_unload_request(u->module); -} - -static void calc_sine(float *f, size_t l, float freq) { - size_t i; - - l /= sizeof(float); - - for (i = 0; i < l; i++) - f[i] = (float) sin((double) i/l*M_PI*2*freq)/2; -} - -int pa__init(pa_core *c, pa_module*m) { - pa_modargs *ma = NULL; - struct userdata *u; - pa_sink *sink; - const char *sink_name; - pa_sample_spec ss; - uint32_t frequency; - char t[256]; - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": Failed to parse module arguments\n"); - goto fail; - } - - m->userdata = u = pa_xmalloc(sizeof(struct userdata)); - u->core = c; - u->module = m; - u->sink_input = NULL; - u->memblock = NULL; - - sink_name = pa_modargs_get_value(ma, "sink", NULL); - - if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) { - pa_log(__FILE__": No such sink.\n"); - goto fail; - } - - ss.format = PA_SAMPLE_FLOAT32; - ss.rate = sink->sample_spec.rate; - ss.channels = 1; - - frequency = 440; - if (pa_modargs_get_value_u32(ma, "frequency", &frequency) < 0 || frequency < 1 || frequency > ss.rate/2) { - pa_log(__FILE__": Invalid frequency specification\n"); - goto fail; - } - - u->memblock = pa_memblock_new(pa_bytes_per_second(&ss), c->memblock_stat); - calc_sine(u->memblock->data, u->memblock->length, frequency); - - snprintf(t, sizeof(t), "Sine Generator at %u Hz", frequency); - if (!(u->sink_input = pa_sink_input_new(sink, __FILE__, t, &ss, NULL, 0, -1))) - goto fail; - - u->sink_input->peek = sink_input_peek; - u->sink_input->drop = sink_input_drop; - u->sink_input->kill = sink_input_kill; - u->sink_input->userdata = u; - u->sink_input->owner = m; - - u->peek_index = 0; - - pa_modargs_free(ma); - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - pa__done(c, m); - return -1; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata *u = m->userdata; - assert(c && m); - - if (!u) - return; - - if (u->sink_input) { - pa_sink_input_disconnect(u->sink_input); - pa_sink_input_unref(u->sink_input); - } - - if (u->memblock) - pa_memblock_unref(u->memblock); - pa_xfree(u); -} - diff --git a/polyp/module-solaris.c b/polyp/module-solaris.c deleted file mode 100644 index f85e71df..00000000 --- a/polyp/module-solaris.c +++ /dev/null @@ -1,486 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "iochannel.h" -#include "sink.h" -#include "source.h" -#include "module.h" -#include "sample-util.h" -#include "util.h" -#include "modargs.h" -#include "xmalloc.h" -#include "log.h" -#include "mainloop-signal.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= source_name= device= record= playback= format= channels= rate= buffer_size=") - -struct userdata { - pa_sink *sink; - pa_source *source; - pa_iochannel *io; - pa_core *core; - pa_signal_event *sig; - - pa_memchunk memchunk, silence; - - uint32_t sample_size; - uint32_t buffer_size; - unsigned int written_bytes, read_bytes; - - int fd; - pa_module *module; -}; - -static const char* const valid_modargs[] = { - "sink_name", - "source_name", - "device", - "record", - "playback", - "buffer_size", - "format", - "rate", - "channels", - NULL -}; - -#define DEFAULT_SINK_NAME "solaris_output" -#define DEFAULT_SOURCE_NAME "solaris_input" -#define DEFAULT_DEVICE "/dev/audio" - -#define CHUNK_SIZE 2048 - -static void update_usage(struct userdata *u) { - pa_module_set_used(u->module, - (u->sink ? pa_idxset_size(u->sink->inputs) : 0) + - (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0) + - (u->source ? pa_idxset_size(u->source->outputs) : 0)); -} - -static void do_write(struct userdata *u) { - audio_info_t info; - int err; - pa_memchunk *memchunk; - size_t len; - ssize_t r; - - assert(u); - - if (!u->sink || !pa_iochannel_is_writable(u->io)) - return; - - update_usage(u); - - err = ioctl(u->fd, AUDIO_GETINFO, &info); - assert(err >= 0); - - /* - * Since we cannot modify the size of the output buffer we fake it - * by not filling it more than u->buffer_size. - */ - len = u->buffer_size; - len -= u->written_bytes - (info.play.samples * u->sample_size); - - /* - * Do not fill more than half the buffer in one chunk since we only - * get notifications upon completion of entire chunks. - */ - if (len > (u->buffer_size / 2)) - len = u->buffer_size / 2; - - if (len < u->sample_size) - return; - - memchunk = &u->memchunk; - - if (!memchunk->length) - if (pa_sink_render(u->sink, len, memchunk) < 0) - memchunk = &u->silence; - - assert(memchunk->memblock); - assert(memchunk->memblock->data); - assert(memchunk->length); - - if (memchunk->length < len) - len = memchunk->length; - - if ((r = pa_iochannel_write(u->io, (uint8_t*) memchunk->memblock->data + memchunk->index, len)) < 0) { - pa_log(__FILE__": write() failed: %s\n", strerror(errno)); - return; - } - - if (memchunk == &u->silence) - assert(r % u->sample_size == 0); - else { - u->memchunk.index += r; - u->memchunk.length -= r; - - if (u->memchunk.length <= 0) { - pa_memblock_unref(u->memchunk.memblock); - u->memchunk.memblock = NULL; - } - } - - u->written_bytes += r; - - /* - * Write 0 bytes which will generate a SIGPOLL when "played". - */ - if (write(u->fd, NULL, 0) < 0) { - pa_log(__FILE__": write() failed: %s\n", strerror(errno)); - return; - } -} - -static void do_read(struct userdata *u) { - pa_memchunk memchunk; - int err, l; - ssize_t r; - assert(u); - - if (!u->source || !pa_iochannel_is_readable(u->io)) - return; - - update_usage(u); - - err = ioctl(u->fd, I_NREAD, &l); - assert(err >= 0); - - memchunk.memblock = pa_memblock_new(l, u->core->memblock_stat); - assert(memchunk.memblock); - if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) { - pa_memblock_unref(memchunk.memblock); - if (errno != EAGAIN) - pa_log(__FILE__": read() failed: %s\n", strerror(errno)); - return; - } - - assert(r <= (ssize_t) memchunk.memblock->length); - memchunk.length = memchunk.memblock->length = r; - memchunk.index = 0; - - pa_source_post(u->source, &memchunk); - pa_memblock_unref(memchunk.memblock); - - u->read_bytes += r; -} - -static void io_callback(pa_iochannel *io, void*userdata) { - struct userdata *u = userdata; - assert(u); - do_write(u); - do_read(u); -} - -void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) { - struct userdata *u = userdata; - assert(u); - do_write(u); -} - -static pa_usec_t sink_get_latency_cb(pa_sink *s) { - pa_usec_t r = 0; - audio_info_t info; - int err; - struct userdata *u = s->userdata; - assert(s && u && u->sink); - - err = ioctl(u->fd, AUDIO_GETINFO, &info); - assert(err >= 0); - - r += pa_bytes_to_usec(u->written_bytes, &s->sample_spec); - r -= pa_bytes_to_usec(info.play.samples * u->sample_size, &s->sample_spec); - - if (u->memchunk.memblock) - r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec); - - return r; -} - -static pa_usec_t source_get_latency_cb(pa_source *s) { - pa_usec_t r = 0; - struct userdata *u = s->userdata; - audio_info_t info; - int err; - assert(s && u && u->source); - - err = ioctl(u->fd, AUDIO_GETINFO, &info); - assert(err >= 0); - - r += pa_bytes_to_usec(info.record.samples * u->sample_size, &s->sample_spec); - r -= pa_bytes_to_usec(u->read_bytes, &s->sample_spec); - - return r; -} - -static int pa_solaris_auto_format(int fd, int mode, pa_sample_spec *ss) { - audio_info_t info; - - AUDIO_INITINFO(&info); - - if (mode != O_RDONLY) { - info.play.sample_rate = ss->rate; - info.play.channels = ss->channels; - switch (ss->format) { - case PA_SAMPLE_U8: - info.play.precision = 8; - info.play.encoding = AUDIO_ENCODING_LINEAR; - break; - case PA_SAMPLE_ALAW: - info.play.precision = 8; - info.play.encoding = AUDIO_ENCODING_ALAW; - break; - case PA_SAMPLE_ULAW: - info.play.precision = 8; - info.play.encoding = AUDIO_ENCODING_ULAW; - break; - case PA_SAMPLE_S16NE: - info.play.precision = 16; - info.play.encoding = AUDIO_ENCODING_LINEAR; - break; - default: - return -1; - } - } - - if (mode != O_WRONLY) { - info.record.sample_rate = ss->rate; - info.record.channels = ss->channels; - switch (ss->format) { - case PA_SAMPLE_U8: - info.record.precision = 8; - info.record.encoding = AUDIO_ENCODING_LINEAR; - break; - case PA_SAMPLE_ALAW: - info.record.precision = 8; - info.record.encoding = AUDIO_ENCODING_ALAW; - break; - case PA_SAMPLE_ULAW: - info.record.precision = 8; - info.record.encoding = AUDIO_ENCODING_ULAW; - break; - case PA_SAMPLE_S16NE: - info.record.precision = 16; - info.record.encoding = AUDIO_ENCODING_LINEAR; - break; - default: - return -1; - } - } - - if (ioctl(fd, AUDIO_SETINFO, &info) < 0) { - if (errno == EINVAL) - pa_log(__FILE__": AUDIO_SETINFO: Unsupported sample format.\n"); - else - pa_log(__FILE__": AUDIO_SETINFO: %s\n", strerror(errno)); - return -1; - } - - return 0; -} - -static int pa_solaris_set_buffer(int fd, int buffer_size) { - audio_info_t info; - - AUDIO_INITINFO(&info); - - info.record.buffer_size = buffer_size; - - if (ioctl(fd, AUDIO_SETINFO, &info) < 0) { - if (errno == EINVAL) - pa_log(__FILE__": AUDIO_SETINFO: Unsupported buffer size.\n"); - else - pa_log(__FILE__": AUDIO_SETINFO: %s\n", strerror(errno)); - return -1; - } - - return 0; -} - -int pa__init(pa_core *c, pa_module*m) { - struct userdata *u = NULL; - const char *p; - int fd = -1; - int buffer_size; - int mode; - int record = 1, playback = 1; - pa_sample_spec ss; - pa_modargs *ma = NULL; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": failed to parse module arguments.\n"); - goto fail; - } - - if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) { - pa_log(__FILE__": record= and playback= expect numeric argument.\n"); - goto fail; - } - - if (!playback && !record) { - pa_log(__FILE__": neither playback nor record enabled for device.\n"); - goto fail; - } - - mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0)); - - buffer_size = 16384; - if (pa_modargs_get_value_s32(ma, "buffer_size", &buffer_size) < 0) { - pa_log(__FILE__": failed to parse buffer size argument\n"); - goto fail; - } - - ss = c->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &ss) < 0) { - pa_log(__FILE__": failed to parse sample specification\n"); - goto fail; - } - - if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0) - goto fail; - - pa_log_info(__FILE__": device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR")); - - if (pa_solaris_auto_format(fd, mode, &ss) < 0) - goto fail; - - if ((mode != O_WRONLY) && (buffer_size >= 1)) - if (pa_solaris_set_buffer(fd, buffer_size) < 0) - goto fail; - - u = pa_xmalloc(sizeof(struct userdata)); - u->core = c; - - if (mode != O_WRONLY) { - u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL); - assert(u->source); - u->source->userdata = u; - u->source->get_latency = source_get_latency_cb; - pa_source_set_owner(u->source, m); - u->source->description = pa_sprintf_malloc("Solaris PCM on '%s'", p); - } else - u->source = NULL; - - if (mode != O_RDONLY) { - u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL); - assert(u->sink); - u->sink->get_latency = sink_get_latency_cb; - u->sink->userdata = u; - pa_sink_set_owner(u->sink, m); - u->sink->description = pa_sprintf_malloc("Solaris PCM on '%s'", p); - } else - u->sink = NULL; - - assert(u->source || u->sink); - - u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0); - assert(u->io); - pa_iochannel_set_callback(u->io, io_callback, u); - u->fd = fd; - - u->memchunk.memblock = NULL; - u->memchunk.length = 0; - u->sample_size = pa_frame_size(&ss); - u->buffer_size = buffer_size; - - u->silence.memblock = pa_memblock_new(u->silence.length = CHUNK_SIZE, u->core->memblock_stat); - assert(u->silence.memblock); - pa_silence_memblock(u->silence.memblock, &ss); - u->silence.index = 0; - - u->written_bytes = 0; - u->read_bytes = 0; - - u->module = m; - m->userdata = u; - - u->sig = pa_signal_new(SIGPOLL, sig_callback, u); - assert(u->sig); - ioctl(u->fd, I_SETSIG, S_MSG); - - pa_modargs_free(ma); - - return 0; - -fail: - if (fd >= 0) - close(fd); - - if (ma) - pa_modargs_free(ma); - - return -1; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata *u; - assert(c && m); - - if (!(u = m->userdata)) - return; - - ioctl(u->fd, I_SETSIG, 0); - pa_signal_free(u->sig); - - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); - if (u->silence.memblock) - pa_memblock_unref(u->silence.memblock); - - if (u->sink) { - pa_sink_disconnect(u->sink); - pa_sink_unref(u->sink); - } - - if (u->source) { - pa_source_disconnect(u->source); - pa_source_unref(u->source); - } - - pa_iochannel_free(u->io); - pa_xfree(u); -} diff --git a/polyp/module-tunnel.c b/polyp/module-tunnel.c deleted file mode 100644 index c088dae0..00000000 --- a/polyp/module-tunnel.c +++ /dev/null @@ -1,688 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include - -#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=
sink= cookie= format= channels= rate= sink_name=") -#else -#include "module-tunnel-source-symdef.h" -PA_MODULE_DESCRIPTION("Tunnel module for sources") -PA_MODULE_USAGE("server=
source= cookie= format= channels= rate= source_name=") -#endif - -PA_MODULE_AUTHOR("Lennart Poettering") -PA_MODULE_VERSION(PACKAGE_VERSION) - -#define DEFAULT_SINK_NAME "tunnel" -#define DEFAULT_SOURCE_NAME "tunnel" - -#define DEFAULT_TLENGTH (44100*2*2/10) //(10240*8) -#define DEFAULT_MAXLENGTH ((DEFAULT_TLENGTH*3)/2) -#define DEFAULT_MINREQ 512 -#define DEFAULT_PREBUF (DEFAULT_TLENGTH-DEFAULT_MINREQ) -#define DEFAULT_FRAGSIZE 1024 - -#define DEFAULT_TIMEOUT 5 - -#define LATENCY_INTERVAL 10 - -static const char* const valid_modargs[] = { - "server", - "cookie", - "format", - "channels", - "rate", -#ifdef TUNNEL_SINK - "sink_name", - "sink", -#else - "source_name", - "source", -#endif - NULL, -}; - -static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); - -#ifdef TUNNEL_SINK -static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -#endif - -static const pa_pdispatch_callback command_table[PA_COMMAND_MAX] = { -#ifdef TUNNEL_SINK - [PA_COMMAND_REQUEST] = command_request, -#endif - [PA_COMMAND_PLAYBACK_STREAM_KILLED] = command_stream_killed, - [PA_COMMAND_RECORD_STREAM_KILLED] = command_stream_killed -}; - -struct userdata { - pa_socket_client *client; - pa_pstream *pstream; - pa_pdispatch *pdispatch; - - char *server_name; -#ifdef TUNNEL_SINK - char *sink_name; - pa_sink *sink; - uint32_t requested_bytes; -#else - char *source_name; - pa_source *source; -#endif - - pa_module *module; - pa_core *core; - - uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH]; - - uint32_t ctag; - uint32_t device_index; - uint32_t channel; - - pa_usec_t host_latency; - - pa_time_event *time_event; - - int auth_cookie_in_property; -}; - -static void close_stuff(struct userdata *u) { - assert(u); - - if (u->pstream) { - pa_pstream_close(u->pstream); - pa_pstream_unref(u->pstream); - u->pstream = NULL; - } - - if (u->pdispatch) { - pa_pdispatch_unref(u->pdispatch); - u->pdispatch = NULL; - } - - if (u->client) { - pa_socket_client_unref(u->client); - u->client = NULL; - } - -#ifdef TUNNEL_SINK - if (u->sink) { - pa_sink_disconnect(u->sink); - pa_sink_unref(u->sink); - u->sink = NULL; - } -#else - if (u->source) { - pa_source_disconnect(u->source); - pa_source_unref(u->source); - u->source = NULL; - } -#endif - - if (u->time_event) { - u->core->mainloop->time_free(u->time_event); - u->time_event = NULL; - } -} - -static void die(struct userdata *u) { - assert(u); - close_stuff(u); - pa_module_unload_request(u->module); -} - -static void command_stream_killed(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - struct userdata *u = userdata; - assert(pd && t && u && u->pdispatch == pd); - - pa_log(__FILE__": stream killed\n"); - die(u); -} - -#ifdef TUNNEL_SINK -static void send_prebuf_request(struct userdata *u) { - pa_tagstruct *t; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_PREBUF_PLAYBACK_STREAM); - pa_tagstruct_putu32(t, u->ctag++); - pa_tagstruct_putu32(t, u->channel); - pa_pstream_send_tagstruct(u->pstream, t); -} - -static void send_bytes(struct userdata *u) { - assert(u); - - if (!u->pstream) - return; - - while (u->requested_bytes > 0) { - pa_memchunk chunk; - if (pa_sink_render(u->sink, u->requested_bytes, &chunk) < 0) { - - - if (u->requested_bytes >= DEFAULT_TLENGTH-DEFAULT_PREBUF) - send_prebuf_request(u); - - return; - } - - pa_pstream_send_memblock(u->pstream, u->channel, 0, &chunk); - pa_memblock_unref(chunk.memblock); - - if (chunk.length > u->requested_bytes) - u->requested_bytes = 0; - else - u->requested_bytes -= chunk.length; - } -} - -static void command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - struct userdata *u = userdata; - uint32_t bytes, channel; - assert(pd && command == PA_COMMAND_REQUEST && t && u && u->pdispatch == pd); - - if (pa_tagstruct_getu32(t, &channel) < 0 || - pa_tagstruct_getu32(t, &bytes) < 0 || - !pa_tagstruct_eof(t)) { - pa_log(__FILE__": invalid protocol reply\n"); - die(u); - return; - } - - if (channel != u->channel) { - pa_log(__FILE__": recieved data for invalid channel\n"); - die(u); - return; - } - - u->requested_bytes += bytes; - send_bytes(u); -} - -#endif - -static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - struct userdata *u = userdata; - pa_usec_t buffer_usec, sink_usec, source_usec, transport_usec; - int playing; - uint32_t queue_length; - uint64_t counter; - struct timeval local, remote, now; - assert(pd && u); - - if (command != PA_COMMAND_REPLY) { - if (command == PA_COMMAND_ERROR) - pa_log(__FILE__": failed to get latency.\n"); - else - pa_log(__FILE__": protocol error.\n"); - die(u); - return; - } - - if (pa_tagstruct_get_usec(t, &buffer_usec) < 0 || - pa_tagstruct_get_usec(t, &sink_usec) < 0 || - pa_tagstruct_get_usec(t, &source_usec) < 0 || - pa_tagstruct_get_boolean(t, &playing) < 0 || - pa_tagstruct_getu32(t, &queue_length) < 0 || - pa_tagstruct_get_timeval(t, &local) < 0 || - pa_tagstruct_get_timeval(t, &remote) < 0 || - pa_tagstruct_getu64(t, &counter) < 0 || - !pa_tagstruct_eof(t)) { - pa_log(__FILE__": invalid reply.\n"); - die(u); - return; - } - - pa_gettimeofday(&now); - - if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) { - /* local and remote seem to have synchronized clocks */ -#ifdef TUNNEL_SINK - transport_usec = pa_timeval_diff(&remote, &local); -#else - transport_usec = pa_timeval_diff(&now, &remote); -#endif - } else - transport_usec = pa_timeval_diff(&now, &local)/2; - -#ifdef TUNNEL_SINK - u->host_latency = sink_usec + transport_usec; -#else - u->host_latency = source_usec + transport_usec; - if (u->host_latency > sink_usec) - u->host_latency -= sink_usec; - else - u->host_latency = 0; -#endif - -/* pa_log(__FILE__": estimated host latency: %0.0f usec\n", (double) u->host_latency); */ -} - -static void request_latency(struct userdata *u) { - pa_tagstruct *t; - struct timeval now; - uint32_t tag; - assert(u); - - t = pa_tagstruct_new(NULL, 0); -#ifdef TUNNEL_SINK - pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY); -#else - pa_tagstruct_putu32(t, PA_COMMAND_GET_RECORD_LATENCY); -#endif - pa_tagstruct_putu32(t, tag = u->ctag++); - pa_tagstruct_putu32(t, u->channel); - - pa_gettimeofday(&now); - pa_tagstruct_put_timeval(t, &now); - pa_tagstruct_putu64(t, 0); - - pa_pstream_send_tagstruct(u->pstream, t); - pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u); -} - -static void create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - struct userdata *u = userdata; - assert(pd && u && u->pdispatch == pd); - - if (command != PA_COMMAND_REPLY) { - if (command == PA_COMMAND_ERROR) - pa_log(__FILE__": failed to create stream.\n"); - else - pa_log(__FILE__": protocol error.\n"); - die(u); - return; - } - - if (pa_tagstruct_getu32(t, &u->channel) < 0 || - pa_tagstruct_getu32(t, &u->device_index) < 0 || -#ifdef TUNNEL_SINK - pa_tagstruct_getu32(t, &u->requested_bytes) < 0 || -#endif - !pa_tagstruct_eof(t)) { - pa_log(__FILE__": invalid reply.\n"); - die(u); - return; - } - - request_latency(u); -#ifdef TUNNEL_SINK - send_bytes(u); -#endif -} - -static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct userdata *u = userdata; - pa_tagstruct *reply; - char name[256], un[128], hn[128]; - assert(pd && u && u->pdispatch == pd); - - if (command != PA_COMMAND_REPLY || !pa_tagstruct_eof(t)) { - if (command == PA_COMMAND_ERROR) - pa_log(__FILE__": failed to authenticate\n"); - else - pa_log(__FILE__": protocol error.\n"); - die(u); - return; - } -#ifdef TUNNEL_SINK - snprintf(name, sizeof(name), "Tunnel from host '%s', user '%s', sink '%s'", - pa_get_host_name(hn, sizeof(hn)), - pa_get_user_name(un, sizeof(un)), - u->sink->name); -#else - snprintf(name, sizeof(name), "Tunnel from host '%s', user '%s', source '%s'", - pa_get_host_name(hn, sizeof(hn)), - pa_get_user_name(un, sizeof(un)), - u->source->name); -#endif - - reply = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME); - pa_tagstruct_putu32(reply, tag = u->ctag++); - pa_tagstruct_puts(reply, name); - pa_pstream_send_tagstruct(u->pstream, reply); - /* We ignore the server's reply here */ - - reply = pa_tagstruct_new(NULL, 0); -#ifdef TUNNEL_SINK - pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_PLAYBACK_STREAM); - pa_tagstruct_putu32(reply, tag = u->ctag++); - pa_tagstruct_puts(reply, name); - pa_tagstruct_put_sample_spec(reply, &u->sink->sample_spec); - pa_tagstruct_putu32(reply, PA_INVALID_INDEX); - pa_tagstruct_puts(reply, u->sink_name); - pa_tagstruct_putu32(reply, DEFAULT_MAXLENGTH); - pa_tagstruct_put_boolean(reply, 0); - pa_tagstruct_putu32(reply, DEFAULT_TLENGTH); - pa_tagstruct_putu32(reply, DEFAULT_PREBUF); - pa_tagstruct_putu32(reply, DEFAULT_MINREQ); - pa_tagstruct_putu32(reply, PA_VOLUME_NORM); -#else - pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_RECORD_STREAM); - pa_tagstruct_putu32(reply, tag = u->ctag++); - pa_tagstruct_puts(reply, name); - pa_tagstruct_put_sample_spec(reply, &u->source->sample_spec); - pa_tagstruct_putu32(reply, PA_INVALID_INDEX); - pa_tagstruct_puts(reply, u->source_name); - pa_tagstruct_putu32(reply, DEFAULT_MAXLENGTH); - pa_tagstruct_put_boolean(reply, 0); - pa_tagstruct_putu32(reply, DEFAULT_FRAGSIZE); -#endif - - pa_pstream_send_tagstruct(u->pstream, reply); - pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u); -} - -static void pstream_die_callback(pa_pstream *p, void *userdata) { - struct userdata *u = userdata; - assert(p && u); - - pa_log(__FILE__": stream died.\n"); - die(u); -} - - -static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) { - struct userdata *u = userdata; - assert(p && packet && u); - - if (pa_pdispatch_run(u->pdispatch, packet, u) < 0) { - pa_log(__FILE__": invalid packet\n"); - die(u); - } -} - -#ifndef TUNNEL_SINK -static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata) { - struct userdata *u = userdata; - assert(p && chunk && u); - - if (channel != u->channel) { - pa_log(__FILE__": recieved memory block on bad channel.\n"); - die(u); - return; - } - - pa_source_post(u->source, chunk); -} -#endif - -static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) { - struct userdata *u = userdata; - pa_tagstruct *t; - uint32_t tag; - assert(sc && u && u->client == sc); - - pa_socket_client_unref(u->client); - u->client = NULL; - - if (!io) { - pa_log(__FILE__": connection failed.\n"); - pa_module_unload_request(u->module); - return; - } - - u->pstream = pa_pstream_new(u->core->mainloop, io, u->core->memblock_stat); - u->pdispatch = pa_pdispatch_new(u->core->mainloop, command_table, PA_COMMAND_MAX); - - pa_pstream_set_die_callback(u->pstream, pstream_die_callback, u); - pa_pstream_set_recieve_packet_callback(u->pstream, pstream_packet_callback, u); -#ifndef TUNNEL_SINK - pa_pstream_set_recieve_memblock_callback(u->pstream, pstream_memblock_callback, u); -#endif - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_AUTH); - pa_tagstruct_putu32(t, tag = u->ctag++); - pa_tagstruct_put_arbitrary(t, u->auth_cookie, sizeof(u->auth_cookie)); - pa_pstream_send_tagstruct(u->pstream, t); - pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, u); - -} - -#ifdef TUNNEL_SINK -static void sink_notify(pa_sink*sink) { - struct userdata *u; - assert(sink && sink->userdata); - u = sink->userdata; - - send_bytes(u); -} - -static pa_usec_t sink_get_latency(pa_sink *sink) { - struct userdata *u; - uint32_t l; - pa_usec_t usec = 0; - assert(sink && sink->userdata); - u = sink->userdata; - - l = DEFAULT_TLENGTH; - - if (l > u->requested_bytes) { - l -= u->requested_bytes; - usec += pa_bytes_to_usec(l, &u->sink->sample_spec); - } - - usec += u->host_latency; - - return usec; -} -#else -static pa_usec_t source_get_latency(pa_source *source) { - struct userdata *u; - assert(source && source->userdata); - u = source->userdata; - - return u->host_latency; -} -#endif - -static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { - struct userdata *u = userdata; - struct timeval ntv; - assert(m && e && u); - - request_latency(u); - - pa_gettimeofday(&ntv); - ntv.tv_sec += LATENCY_INTERVAL; - m->time_restart(e, &ntv); -} - -static int load_key(struct userdata *u, const char*fn) { - assert(u); - - u->auth_cookie_in_property = 0; - - if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) { - pa_log_debug(__FILE__": using already loaded auth cookie.\n"); - pa_authkey_prop_ref(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME); - u->auth_cookie_in_property = 1; - return 0; - } - - if (!fn) - fn = PA_NATIVE_COOKIE_FILE; - - if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0) - return -1; - - pa_log_debug(__FILE__": loading cookie from disk.\n"); - - if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) - u->auth_cookie_in_property = 1; - - return 0; -} - -int pa__init(pa_core *c, pa_module*m) { - pa_modargs *ma = NULL; - struct userdata *u = NULL; - pa_sample_spec ss; - struct timeval ntv; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": failed to parse module arguments\n"); - goto fail; - } - - u = pa_xmalloc(sizeof(struct userdata)); - m->userdata = u; - u->module = m; - u->core = c; - u->client = NULL; - u->pdispatch = NULL; - u->pstream = NULL; - u->server_name = NULL; -#ifdef TUNNEL_SINK - u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));; - u->sink = NULL; - u->requested_bytes = 0; -#else - u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));; - u->source = NULL; -#endif - u->ctag = 1; - u->device_index = u->channel = PA_INVALID_INDEX; - u->host_latency = 0; - u->auth_cookie_in_property = 0; - u->time_event = NULL; - - if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0) - goto fail; - - if (!(u->server_name = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL)))) { - pa_log(__FILE__": no server specified.\n"); - goto fail; - } - - ss = c->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &ss) < 0) { - pa_log(__FILE__": invalid sample format specification\n"); - goto fail; - } - - if (!(u->client = pa_socket_client_new_string(c->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) { - pa_log(__FILE__": failed to connect to server '%s'\n", u->server_name); - goto fail; - } - - if (!u->client) - goto fail; - - pa_socket_client_set_callback(u->client, on_connection, u); - -#ifdef TUNNEL_SINK - if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) { - pa_log(__FILE__": failed to create sink.\n"); - goto fail; - } - - u->sink->notify = sink_notify; - u->sink->get_latency = sink_get_latency; - u->sink->userdata = u; - u->sink->description = pa_sprintf_malloc("Tunnel to '%s%s%s'", u->sink_name ? u->sink_name : "", u->sink_name ? "@" : "", u->server_name); - - pa_sink_set_owner(u->sink, m); -#else - if (!(u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL))) { - pa_log(__FILE__": failed to create source.\n"); - goto fail; - } - - u->source->get_latency = source_get_latency; - u->source->userdata = u; - u->source->description = pa_sprintf_malloc("Tunnel to '%s%s%s'", u->source_name ? u->source_name : "", u->source_name ? "@" : "", u->server_name); - - pa_source_set_owner(u->source, m); -#endif - - pa_gettimeofday(&ntv); - ntv.tv_sec += LATENCY_INTERVAL; - u->time_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, u); - - pa_modargs_free(ma); - - return 0; - -fail: - pa__done(c, m); - - if (ma) - pa_modargs_free(ma); - return -1; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata* u; - assert(c && m); - - if (!(u = m->userdata)) - return; - - close_stuff(u); - - if (u->auth_cookie_in_property) - pa_authkey_prop_unref(c, PA_NATIVE_COOKIE_PROPERTY_NAME); - -#ifdef TUNNEL_SINK - pa_xfree(u->sink_name); -#else - pa_xfree(u->source_name); -#endif - pa_xfree(u->server_name); - - pa_xfree(u); -} - - diff --git a/polyp/module-waveout.c b/polyp/module-waveout.c deleted file mode 100644 index e9d9f12e..00000000 --- a/polyp/module-waveout.c +++ /dev/null @@ -1,581 +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 -#endif - -#include -#include -#include - -#include "sink.h" -#include "source.h" -#include "module.h" -#include "mainloop-api.h" -#include "modargs.h" -#include "sample-util.h" -#include "util.h" -#include "log.h" -#include "xmalloc.h" -#include "module-waveout-symdef.h" - -PA_MODULE_AUTHOR("Pierre Ossman") -PA_MODULE_DESCRIPTION("Windows waveOut Sink/Source") -PA_MODULE_VERSION(PACKAGE_VERSION) -PA_MODULE_USAGE("sink_name= source_name= record= playback= format= channels= rate= fragments= fragment_size=") - -#define DEFAULT_SINK_NAME "wave_output" -#define DEFAULT_SOURCE_NAME "wave_input" - -struct userdata { - pa_sink *sink; - pa_source *source; - pa_core *core; - pa_time_event *event; - pa_defer_event *defer; - pa_usec_t poll_timeout; - - uint32_t fragments, fragment_size; - - uint32_t free_ofrags, free_ifrags; - - DWORD written_bytes; - - int cur_ohdr, cur_ihdr; - unsigned int oremain; - WAVEHDR *ohdrs, *ihdrs; - pa_memchunk silence; - - HWAVEOUT hwo; - HWAVEIN hwi; - pa_module *module; - - CRITICAL_SECTION crit; -}; - -static const char* const valid_modargs[] = { - "sink_name", - "source_name", - "record", - "playback", - "fragments", - "fragment_size", - "format", - "rate", - "channels", - NULL -}; - -static void update_usage(struct userdata *u) { - pa_module_set_used(u->module, - (u->sink ? pa_idxset_size(u->sink->inputs) : 0) + - (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0) + - (u->source ? pa_idxset_size(u->source->outputs) : 0)); -} - -static void do_write(struct userdata *u) -{ - uint32_t free_frags, remain; - pa_memchunk memchunk, *cur_chunk; - WAVEHDR *hdr; - MMRESULT res; - - if (!u->sink) - return; - - EnterCriticalSection(&u->crit); - - free_frags = u->free_ofrags; - u->free_ofrags = 0; - - LeaveCriticalSection(&u->crit); - - while (free_frags) { - hdr = &u->ohdrs[u->cur_ohdr]; - if (hdr->dwFlags & WHDR_PREPARED) - waveOutUnprepareHeader(u->hwo, hdr, sizeof(WAVEHDR)); - - remain = u->oremain; - while (remain) { - cur_chunk = &memchunk; - - if (pa_sink_render(u->sink, remain, cur_chunk) < 0) { - /* - * Don't fill with silence unless we're getting close to - * underflowing. - */ - if (free_frags > u->fragments/2) - cur_chunk = &u->silence; - else { - EnterCriticalSection(&u->crit); - - u->free_ofrags += free_frags; - - LeaveCriticalSection(&u->crit); - - u->oremain = remain; - return; - } - } - - assert(cur_chunk->memblock); - assert(cur_chunk->memblock->data); - assert(cur_chunk->length); - - memcpy(hdr->lpData + u->fragment_size - remain, - (char*)cur_chunk->memblock->data + cur_chunk->index, - (cur_chunk->length < remain)?cur_chunk->length:remain); - - remain -= (cur_chunk->length < remain)?cur_chunk->length:remain; - - if (cur_chunk != &u->silence) { - pa_memblock_unref(cur_chunk->memblock); - cur_chunk->memblock = NULL; - } - } - - res = waveOutPrepareHeader(u->hwo, hdr, sizeof(WAVEHDR)); - if (res != MMSYSERR_NOERROR) { - pa_log_error(__FILE__ ": ERROR: Unable to prepare waveOut block: %d\n", - res); - } - res = waveOutWrite(u->hwo, hdr, sizeof(WAVEHDR)); - if (res != MMSYSERR_NOERROR) { - pa_log_error(__FILE__ ": ERROR: Unable to write waveOut block: %d\n", - res); - } - - u->written_bytes += u->fragment_size; - - free_frags--; - u->cur_ohdr++; - u->cur_ohdr %= u->fragments; - u->oremain = u->fragment_size; - } -} - -static void do_read(struct userdata *u) -{ - uint32_t free_frags; - pa_memchunk memchunk; - WAVEHDR *hdr; - MMRESULT res; - - if (!u->source) - return; - - EnterCriticalSection(&u->crit); - - free_frags = u->free_ifrags; - u->free_ifrags = 0; - - LeaveCriticalSection(&u->crit); - - while (free_frags) { - hdr = &u->ihdrs[u->cur_ihdr]; - if (hdr->dwFlags & WHDR_PREPARED) - waveInUnprepareHeader(u->hwi, hdr, sizeof(WAVEHDR)); - - if (hdr->dwBytesRecorded) { - memchunk.memblock = pa_memblock_new(hdr->dwBytesRecorded, u->core->memblock_stat); - assert(memchunk.memblock); - - memcpy((char*)memchunk.memblock->data, hdr->lpData, hdr->dwBytesRecorded); - - memchunk.length = memchunk.memblock->length = hdr->dwBytesRecorded; - memchunk.index = 0; - - pa_source_post(u->source, &memchunk); - pa_memblock_unref(memchunk.memblock); - } - - res = waveInPrepareHeader(u->hwi, hdr, sizeof(WAVEHDR)); - if (res != MMSYSERR_NOERROR) { - pa_log_error(__FILE__ ": ERROR: Unable to prepare waveIn block: %d\n", - res); - } - res = waveInAddBuffer(u->hwi, hdr, sizeof(WAVEHDR)); - if (res != MMSYSERR_NOERROR) { - pa_log_error(__FILE__ ": ERROR: Unable to add waveIn block: %d\n", - res); - } - - free_frags--; - u->cur_ihdr++; - u->cur_ihdr %= u->fragments; - } -} - -static void poll_cb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) { - struct userdata *u = userdata; - struct timeval ntv; - - assert(u); - - update_usage(u); - - do_write(u); - do_read(u); - - pa_gettimeofday(&ntv); - pa_timeval_add(&ntv, u->poll_timeout); - - a->time_restart(e, &ntv); -} - -static void defer_cb(pa_mainloop_api*a, pa_defer_event *e, void *userdata) { - struct userdata *u = userdata; - - assert(u); - - a->defer_enable(e, 0); - - do_write(u); - do_read(u); -} - -static void CALLBACK chunk_done_cb(HWAVEOUT hwo, UINT msg, DWORD_PTR inst, DWORD param1, DWORD param2) { - struct userdata *u = (struct userdata *)inst; - - if (msg != WOM_DONE) - return; - - EnterCriticalSection(&u->crit); - - u->free_ofrags++; - assert(u->free_ofrags <= u->fragments); - - LeaveCriticalSection(&u->crit); -} - -static void CALLBACK chunk_ready_cb(HWAVEIN hwi, UINT msg, DWORD_PTR inst, DWORD param1, DWORD param2) { - struct userdata *u = (struct userdata *)inst; - - if (msg != WIM_DATA) - return; - - EnterCriticalSection(&u->crit); - - u->free_ifrags++; - assert(u->free_ifrags <= u->fragments); - - LeaveCriticalSection(&u->crit); -} - -static pa_usec_t sink_get_latency_cb(pa_sink *s) { - struct userdata *u = s->userdata; - uint32_t free_frags; - MMTIME mmt; - assert(s && u && u->sink); - - memset(&mmt, 0, sizeof(mmt)); - mmt.wType = TIME_BYTES; - if (waveOutGetPosition(u->hwo, &mmt, sizeof(mmt)) == MMSYSERR_NOERROR) - return pa_bytes_to_usec(u->written_bytes - mmt.u.cb, &s->sample_spec); - else { - EnterCriticalSection(&u->crit); - - free_frags = u->free_ofrags; - - LeaveCriticalSection(&u->crit); - - return pa_bytes_to_usec((u->fragments - free_frags) * u->fragment_size, - &s->sample_spec); - } -} - -static pa_usec_t source_get_latency_cb(pa_source *s) { - pa_usec_t r = 0; - struct userdata *u = s->userdata; - uint32_t free_frags; - assert(s && u && u->sink); - - EnterCriticalSection(&u->crit); - - free_frags = u->free_ifrags; - - LeaveCriticalSection(&u->crit); - - r += pa_bytes_to_usec((free_frags + 1) * u->fragment_size, &s->sample_spec); - - fprintf(stderr, "Latency: %d us\n", (int)r); - - return r; -} - -static void notify_sink_cb(pa_sink *s) { - struct userdata *u = s->userdata; - assert(u); - - u->core->mainloop->defer_enable(u->defer, 1); -} - -static void notify_source_cb(pa_source *s) { - struct userdata *u = s->userdata; - assert(u); - - u->core->mainloop->defer_enable(u->defer, 1); -} - -static int ss_to_waveformat(pa_sample_spec *ss, LPWAVEFORMATEX wf) { - wf->wFormatTag = WAVE_FORMAT_PCM; - - if (ss->channels > 2) { - pa_log_error(__FILE__": ERROR: More than two channels not supported.\n"); - return -1; - } - - wf->nChannels = ss->channels; - - switch (ss->rate) { - case 8000: - case 11025: - case 22005: - case 44100: - break; - default: - pa_log_error(__FILE__": ERROR: Unsupported sample rate.\n"); - return -1; - } - - wf->nSamplesPerSec = ss->rate; - - if (ss->format == PA_SAMPLE_U8) - wf->wBitsPerSample = 8; - else if (ss->format == PA_SAMPLE_S16NE) - wf->wBitsPerSample = 16; - else { - pa_log_error(__FILE__": ERROR: Unsupported sample format.\n"); - return -1; - } - - wf->nBlockAlign = wf->nChannels * wf->wBitsPerSample/8; - wf->nAvgBytesPerSec = wf->nSamplesPerSec * wf->nBlockAlign; - - wf->cbSize = 0; - - return 0; -} - -int pa__init(pa_core *c, pa_module*m) { - struct userdata *u = NULL; - HWAVEOUT hwo = INVALID_HANDLE_VALUE; - HWAVEIN hwi = INVALID_HANDLE_VALUE; - WAVEFORMATEX wf; - int nfrags, frag_size; - int record = 1, playback = 1; - pa_sample_spec ss; - pa_modargs *ma = NULL; - unsigned int i; - struct timeval tv; - - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": failed to parse module arguments.\n"); - goto fail; - } - - if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) { - pa_log(__FILE__": record= and playback= expect boolean argument.\n"); - goto fail; - } - - if (!playback && !record) { - pa_log(__FILE__": neither playback nor record enabled for device.\n"); - goto fail; - } - - nfrags = 20; - frag_size = 1024; - if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) { - pa_log(__FILE__": failed to parse fragments arguments\n"); - goto fail; - } - - ss = c->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &ss) < 0) { - pa_log(__FILE__": failed to parse sample specification\n"); - goto fail; - } - - if (ss_to_waveformat(&ss, &wf) < 0) - goto fail; - - u = pa_xmalloc(sizeof(struct userdata)); - - if (record) { - if (waveInOpen(&hwi, WAVE_MAPPER, &wf, (DWORD_PTR)chunk_ready_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) - goto fail; - if (waveInStart(hwi) != MMSYSERR_NOERROR) - goto fail; - pa_log_debug(__FILE__": Opened waveIn subsystem.\n"); - } - - if (playback) { - if (waveOutOpen(&hwo, WAVE_MAPPER, &wf, (DWORD_PTR)chunk_done_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) - goto fail; - pa_log_debug(__FILE__": Opened waveOut subsystem.\n"); - } - - InitializeCriticalSection(&u->crit); - - if (hwi != INVALID_HANDLE_VALUE) { - u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL); - assert(u->source); - u->source->userdata = u; - u->source->notify = notify_source_cb; - u->source->get_latency = source_get_latency_cb; - pa_source_set_owner(u->source, m); - u->source->description = pa_sprintf_malloc("Windows waveIn PCM"); - } else - u->source = NULL; - - if (hwo != INVALID_HANDLE_VALUE) { - u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL); - assert(u->sink); - u->sink->notify = notify_sink_cb; - u->sink->get_latency = sink_get_latency_cb; - u->sink->userdata = u; - pa_sink_set_owner(u->sink, m); - u->sink->description = pa_sprintf_malloc("Windows waveOut PCM"); - } else - u->sink = NULL; - - assert(u->source || u->sink); - - u->core = c; - u->hwi = hwi; - u->hwo = hwo; - - u->fragments = nfrags; - u->free_ifrags = u->fragments; - u->free_ofrags = u->fragments; - u->fragment_size = frag_size - (frag_size % pa_frame_size(&ss)); - - u->written_bytes = 0; - - u->oremain = u->fragment_size; - - u->poll_timeout = pa_bytes_to_usec(u->fragments * u->fragment_size / 3, &ss); - - pa_gettimeofday(&tv); - pa_timeval_add(&tv, u->poll_timeout); - - u->event = c->mainloop->time_new(c->mainloop, &tv, poll_cb, u); - assert(u->event); - - u->defer = c->mainloop->defer_new(c->mainloop, defer_cb, u); - assert(u->defer); - c->mainloop->defer_enable(u->defer, 0); - - u->cur_ihdr = 0; - u->cur_ohdr = 0; - u->ihdrs = pa_xmalloc0(sizeof(WAVEHDR) * u->fragments); - assert(u->ihdrs); - u->ohdrs = pa_xmalloc0(sizeof(WAVEHDR) * u->fragments); - assert(u->ohdrs); - for (i = 0;i < u->fragments;i++) { - u->ihdrs[i].dwBufferLength = u->fragment_size; - u->ohdrs[i].dwBufferLength = u->fragment_size; - u->ihdrs[i].lpData = pa_xmalloc(u->fragment_size); - assert(u->ihdrs); - u->ohdrs[i].lpData = pa_xmalloc(u->fragment_size); - assert(u->ohdrs); - } - - u->silence.length = u->fragment_size; - u->silence.memblock = pa_memblock_new(u->silence.length, u->core->memblock_stat); - assert(u->silence.memblock); - pa_silence_memblock(u->silence.memblock, &ss); - u->silence.index = 0; - - u->module = m; - m->userdata = u; - - pa_modargs_free(ma); - - return 0; - -fail: - if (hwi != INVALID_HANDLE_VALUE) - waveInClose(hwi); - - if (hwo != INVALID_HANDLE_VALUE) - waveOutClose(hwo); - - if (u) - pa_xfree(u); - - if (ma) - pa_modargs_free(ma); - - return -1; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata *u; - unsigned int i; - - assert(c && m); - - if (!(u = m->userdata)) - return; - - if (u->event) - c->mainloop->time_free(u->event); - - if (u->defer) - c->mainloop->defer_free(u->defer); - - if (u->sink) { - pa_sink_disconnect(u->sink); - pa_sink_unref(u->sink); - } - - if (u->source) { - pa_source_disconnect(u->source); - pa_source_unref(u->source); - } - - if (u->hwi != INVALID_HANDLE_VALUE) { - waveInReset(u->hwi); - waveInClose(u->hwi); - } - - if (u->hwo != INVALID_HANDLE_VALUE) { - waveOutReset(u->hwo); - waveOutClose(u->hwo); - } - - for (i = 0;i < u->fragments;i++) { - pa_xfree(u->ihdrs[i].lpData); - pa_xfree(u->ohdrs[i].lpData); - } - - pa_xfree(u->ihdrs); - pa_xfree(u->ohdrs); - - DeleteCriticalSection(&u->crit); - - pa_xfree(u); -} diff --git a/polyp/module-x11-bell.c b/polyp/module-x11-bell.c deleted file mode 100644 index 4fc4a60d..00000000 --- a/polyp/module-x11-bell.c +++ /dev/null @@ -1,172 +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 -#endif - -#include -#include -#include -#include - -#include -#include - -#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= sample= display=") - -struct userdata { - pa_core *core; - int xkb_event_base; - char *sink_name; - char *scache_item; - Display *display; - - pa_x11_wrapper *x11_wrapper; - pa_x11_client *x11_client; -}; - -static const char* const valid_modargs[] = { - "sink", - "sample", - "display", - NULL -}; - -static int ring_bell(struct userdata *u, int percent) { - pa_sink *s; - pa_cvolume cv; - assert(u); - - if (!(s = pa_namereg_get(u->core, u->sink_name, PA_NAMEREG_SINK, 1))) { - pa_log(__FILE__": Invalid sink: %s\n", u->sink_name); - return -1; - } - - pa_scache_play_item(u->core, u->scache_item, s, pa_cvolume_set(&cv, PA_CHANNELS_MAX, percent*PA_VOLUME_NORM/100)); - return 0; -} - -static int x11_event_callback(pa_x11_wrapper *w, XEvent *e, void *userdata) { - XkbBellNotifyEvent *bne; - struct userdata *u = userdata; - assert(w && e && u && u->x11_wrapper == w); - - if (((XkbEvent*) e)->any.xkb_type != XkbBellNotify) - return 0; - - bne = (XkbBellNotifyEvent*) e; - - if (ring_bell(u, bne->percent) < 0) { - pa_log_info(__FILE__": Ringing bell failed, reverting to X11 device bell.\n"); - XkbForceDeviceBell(pa_x11_wrapper_get_display(w), bne->device, bne->bell_class, bne->bell_id, bne->percent); - } - - return 1; -} - -int pa__init(pa_core *c, pa_module*m) { - struct userdata *u = NULL; - pa_modargs *ma = NULL; - int major, minor; - unsigned int auto_ctrls, auto_values; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": failed to parse module arguments\n"); - goto fail; - } - - m->userdata = u = pa_xmalloc(sizeof(struct userdata)); - u->core = c; - u->scache_item = pa_xstrdup(pa_modargs_get_value(ma, "sample", "x11-bell")); - u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); - u->x11_client = NULL; - - if (!(u->x11_wrapper = pa_x11_wrapper_get(c, pa_modargs_get_value(ma, "display", NULL)))) - goto fail; - - u->display = pa_x11_wrapper_get_display(u->x11_wrapper); - - major = XkbMajorVersion; - minor = XkbMinorVersion; - - if (!XkbLibraryVersion(&major, &minor)) { - pa_log(__FILE__": XkbLibraryVersion() failed\n"); - goto fail; - } - - major = XkbMajorVersion; - minor = XkbMinorVersion; - - - if (!XkbQueryExtension(u->display, NULL, &u->xkb_event_base, NULL, &major, &minor)) { - pa_log(__FILE__": XkbQueryExtension() failed\n"); - goto fail; - } - - XkbSelectEvents(u->display, XkbUseCoreKbd, XkbBellNotifyMask, XkbBellNotifyMask); - auto_ctrls = auto_values = XkbAudibleBellMask; - XkbSetAutoResetControls(u->display, XkbAudibleBellMask, &auto_ctrls, &auto_values); - XkbChangeEnabledControls(u->display, XkbUseCoreKbd, XkbAudibleBellMask, 0); - - u->x11_client = pa_x11_client_new(u->x11_wrapper, x11_event_callback, u); - - pa_modargs_free(ma); - - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - if (m->userdata) - pa__done(c, m); - return -1; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata *u = m->userdata; - assert(c && m && u); - - pa_xfree(u->scache_item); - pa_xfree(u->sink_name); - - if (u->x11_client) - pa_x11_client_free(u->x11_client); - - if (u->x11_wrapper) - pa_x11_wrapper_unref(u->x11_wrapper); - - pa_xfree(u); -} diff --git a/polyp/module-x11-publish.c b/polyp/module-x11-publish.c deleted file mode 100644 index 0116914f..00000000 --- a/polyp/module-x11-publish.c +++ /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 -#endif - -#include -#include -#include -#include -#include - -#include -#include - -#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=") - -static const char* const valid_modargs[] = { - "display", - "sink", - "source", - "cookie", - NULL -}; - -struct userdata { - pa_core *core; - pa_x11_wrapper *x11_wrapper; - Display *display; - char *id; - uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH]; - int auth_cookie_in_property; -}; - -static int load_key(struct userdata *u, const char*fn) { - assert(u); - - u->auth_cookie_in_property = 0; - - if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) { - pa_log_debug(__FILE__": using already loaded auth cookie.\n"); - pa_authkey_prop_ref(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME); - u->auth_cookie_in_property = 1; - return 0; - } - - if (!fn) - fn = PA_NATIVE_COOKIE_FILE; - - if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0) - return -1; - - pa_log_debug(__FILE__": loading cookie from disk.\n"); - - if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) - u->auth_cookie_in_property = 1; - - return 0; -} - -int pa__init(pa_core *c, pa_module*m) { - struct userdata *u; - pa_modargs *ma = NULL; - char hn[256], un[128]; - char hx[PA_NATIVE_COOKIE_LENGTH*2+1]; - const char *t; - char *s; - pa_strlist *l; - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": failed to parse module arguments\n"); - goto fail; - } - - m->userdata = u = pa_xmalloc(sizeof(struct userdata)); - u->core = c; - u->id = NULL; - u->auth_cookie_in_property = 0; - - if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0) - goto fail; - - if (!(u->x11_wrapper = pa_x11_wrapper_get(c, pa_modargs_get_value(ma, "display", NULL)))) - goto fail; - - u->display = pa_x11_wrapper_get_display(u->x11_wrapper); - - if (!(l = pa_property_get(c, PA_NATIVE_SERVER_PROPERTY_NAME))) - goto fail; - - s = pa_strlist_tostring(l); - pa_x11_set_prop(u->display, "POLYP_SERVER", s); - pa_xfree(s); - - if (!pa_get_fqdn(hn, sizeof(hn)) || !pa_get_user_name(un, sizeof(un))) - goto fail; - - u->id = pa_sprintf_malloc("%s@%s/%u", un, hn, (unsigned) getpid()); - pa_x11_set_prop(u->display, "POLYP_ID", u->id); - - if ((t = pa_modargs_get_value(ma, "source", NULL))) - pa_x11_set_prop(u->display, "POLYP_SOURCE", t); - - if ((t = pa_modargs_get_value(ma, "sink", NULL))) - pa_x11_set_prop(u->display, "POLYP_SINK", t); - - pa_x11_set_prop(u->display, "POLYP_COOKIE", pa_hexstr(u->auth_cookie, sizeof(u->auth_cookie), hx, sizeof(hx))); - - pa_modargs_free(ma); - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - pa__done(c, m); - return -1; -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata*u; - assert(c && m); - - if (!(u = m->userdata)) - return; - - if (u->x11_wrapper) { - char t[256]; - - /* Yes, here is a race condition */ - if (!pa_x11_get_prop(u->display, "POLYP_ID", t, sizeof(t)) || strcmp(t, u->id)) - pa_log("WARNING: Polypaudio information vanished from X11!\n"); - else { - pa_x11_del_prop(u->display, "POLYP_ID"); - pa_x11_del_prop(u->display, "POLYP_SERVER"); - pa_x11_del_prop(u->display, "POLYP_SINK"); - pa_x11_del_prop(u->display, "POLYP_SOURCE"); - pa_x11_del_prop(u->display, "POLYP_COOKIE"); - XSync(u->display, False); - } - } - - if (u->x11_wrapper) - pa_x11_wrapper_unref(u->x11_wrapper); - - if (u->auth_cookie_in_property) - pa_authkey_prop_unref(c, PA_NATIVE_COOKIE_PROPERTY_NAME); - - pa_xfree(u->id); - pa_xfree(u); -} - diff --git a/polyp/module-zeroconf-publish.c b/polyp/module-zeroconf-publish.c deleted file mode 100644 index 7e0a2764..00000000 --- a/polyp/module-zeroconf-publish.c +++ /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 -#endif - -#include -#include -#include -#include -#include - -#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=") - -#define SERVICE_NAME_SINK "_polypaudio-sink._tcp" -#define SERVICE_NAME_SOURCE "_polypaudio-source._tcp" -#define SERVICE_NAME_SERVER "_polypaudio-server._tcp" - -static const char* const valid_modargs[] = { - "port", - NULL -}; - -struct service { - sw_discovery_oid oid; - char *name; - int published; /* 0 -> not yet registered, 1 -> registered with data from real device, 2 -> registered with data from autoload device */ - - struct { - int valid; - pa_namereg_type type; - uint32_t index; - } loaded; - - struct { - int valid; - pa_namereg_type type; - uint32_t index; - } autoload; -}; - -struct userdata { - pa_core *core; - pa_howl_wrapper *howl_wrapper; - pa_hashmap *services; - pa_dynarray *sink_dynarray, *source_dynarray, *autoload_dynarray; - pa_subscription *subscription; - - uint16_t port; - sw_discovery_oid server_oid; -}; - -static sw_result publish_reply(sw_discovery discovery, sw_discovery_publish_status status, sw_discovery_oid oid, sw_opaque extra) { - return SW_OKAY; -} - -static void get_service_data(struct userdata *u, struct service *s, pa_sample_spec *ret_ss, char **ret_description, pa_typeid_t *ret_typeid) { - assert(u && s && s->loaded.valid && ret_ss && ret_description && ret_typeid); - - if (s->loaded.type == PA_NAMEREG_SINK) { - pa_sink *sink = pa_idxset_get_by_index(u->core->sinks, s->loaded.index); - assert(sink); - *ret_ss = sink->sample_spec; - *ret_description = sink->description; - *ret_typeid = sink->typeid; - } else if (s->loaded.type == PA_NAMEREG_SOURCE) { - pa_source *source = pa_idxset_get_by_index(u->core->sources, s->loaded.index); - assert(source); - *ret_ss = source->sample_spec; - *ret_description = source->description; - *ret_typeid = source->typeid; - } else - assert(0); -} - -static void txt_record_server_data(pa_core *c, sw_text_record t) { - char s[256]; - assert(c); - - sw_text_record_add_key_and_string_value(t, "server-version", PACKAGE_NAME" "PACKAGE_VERSION); - sw_text_record_add_key_and_string_value(t, "user-name", pa_get_user_name(s, sizeof(s))); - sw_text_record_add_key_and_string_value(t, "fqdn", pa_get_fqdn(s, sizeof(s))); - snprintf(s, sizeof(s), "0x%08x", c->cookie); - sw_text_record_add_key_and_string_value(t, "cookie", s); -} - -static int publish_service(struct userdata *u, struct service *s) { - char t[256]; - char hn[256]; - int r = -1; - sw_text_record txt; - int free_txt = 0; - assert(u && s); - - if ((s->published == 1 && s->loaded.valid) || - (s->published == 2 && s->autoload.valid && !s->loaded.valid)) - return 0; - - if (s->published) { - sw_discovery_cancel(pa_howl_wrapper_get_discovery(u->howl_wrapper), s->oid); - s->published = 0; - } - - snprintf(t, sizeof(t), "Networked Audio Device %s on %s", s->name, pa_get_host_name(hn, sizeof(hn))); - - if (sw_text_record_init(&txt) != SW_OKAY) { - pa_log(__FILE__": sw_text_record_init() failed\n"); - goto finish; - } - free_txt = 1; - - sw_text_record_add_key_and_string_value(txt, "device", s->name); - - txt_record_server_data(u->core, txt); - - if (s->loaded.valid) { - char z[64], *description; - pa_typeid_t typeid; - pa_sample_spec ss; - - get_service_data(u, s, &ss, &description, &typeid); - - snprintf(z, sizeof(z), "%u", ss.rate); - sw_text_record_add_key_and_string_value(txt, "rate", z); - snprintf(z, sizeof(z), "%u", ss.channels); - sw_text_record_add_key_and_string_value(txt, "channels", z); - sw_text_record_add_key_and_string_value(txt, "format", pa_sample_format_to_string(ss.format)); - - sw_text_record_add_key_and_string_value(txt, "description", description); - - snprintf(z, sizeof(z), "0x%8x", typeid); - sw_text_record_add_key_and_string_value(txt, "typeid", z); - - - if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t, - s->loaded.type == PA_NAMEREG_SINK ? SERVICE_NAME_SINK : SERVICE_NAME_SOURCE, - NULL, NULL, u->port, sw_text_record_bytes(txt), sw_text_record_len(txt), - publish_reply, s, &s->oid) != SW_OKAY) { - pa_log(__FILE__": failed to register sink on zeroconf.\n"); - goto finish; - } - - s->published = 1; - } else if (s->autoload.valid) { - - if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t, - s->autoload.type == PA_NAMEREG_SINK ? SERVICE_NAME_SINK : SERVICE_NAME_SOURCE, - NULL, NULL, u->port, sw_text_record_bytes(txt), sw_text_record_len(txt), - publish_reply, s, &s->oid) != SW_OKAY) { - pa_log(__FILE__": failed to register sink on zeroconf.\n"); - goto finish; - } - - s->published = 2; - } - - r = 0; - -finish: - - if (!s->published) { - /* Remove this service */ - pa_hashmap_remove(u->services, s->name); - pa_xfree(s->name); - pa_xfree(s); - } - - if (free_txt) - sw_text_record_fina(txt); - - return r; -} - -struct service *get_service(struct userdata *u, const char *name) { - struct service *s; - - if ((s = pa_hashmap_get(u->services, name))) - return s; - - s = pa_xmalloc(sizeof(struct service)); - s->published = 0; - s->name = pa_xstrdup(name); - s->loaded.valid = s->autoload.valid = 0; - - pa_hashmap_put(u->services, s->name, s); - - return s; -} - -static int publish_sink(struct userdata *u, pa_sink *s) { - struct service *svc; - assert(u && s); - - svc = get_service(u, s->name); - if (svc->loaded.valid) - return 0; - - svc->loaded.valid = 1; - svc->loaded.type = PA_NAMEREG_SINK; - svc->loaded.index = s->index; - - pa_dynarray_put(u->sink_dynarray, s->index, svc); - - return publish_service(u, svc); -} - -static int publish_source(struct userdata *u, pa_source *s) { - struct service *svc; - assert(u && s); - - svc = get_service(u, s->name); - if (svc->loaded.valid) - return 0; - - svc->loaded.valid = 1; - svc->loaded.type = PA_NAMEREG_SOURCE; - svc->loaded.index = s->index; - - pa_dynarray_put(u->source_dynarray, s->index, svc); - - return publish_service(u, svc); -} - -static int publish_autoload(struct userdata *u, pa_autoload_entry *s) { - struct service *svc; - assert(u && s); - - svc = get_service(u, s->name); - if (svc->autoload.valid) - return 0; - - svc->autoload.valid = 1; - svc->autoload.type = s->type; - svc->autoload.index = s->index; - - pa_dynarray_put(u->autoload_dynarray, s->index, svc); - - return publish_service(u, svc); -} - -static int remove_sink(struct userdata *u, uint32_t index) { - struct service *svc; - assert(u && index != PA_INVALID_INDEX); - - if (!(svc = pa_dynarray_get(u->sink_dynarray, index))) - return 0; - - if (!svc->loaded.valid || svc->loaded.type != PA_NAMEREG_SINK) - return 0; - - svc->loaded.valid = 0; - pa_dynarray_put(u->sink_dynarray, index, NULL); - - return publish_service(u, svc); -} - -static int remove_source(struct userdata *u, uint32_t index) { - struct service *svc; - assert(u && index != PA_INVALID_INDEX); - - if (!(svc = pa_dynarray_get(u->source_dynarray, index))) - return 0; - - if (!svc->loaded.valid || svc->loaded.type != PA_NAMEREG_SOURCE) - return 0; - - svc->loaded.valid = 0; - pa_dynarray_put(u->source_dynarray, index, NULL); - - return publish_service(u, svc); -} - -static int remove_autoload(struct userdata *u, uint32_t index) { - struct service *svc; - assert(u && index != PA_INVALID_INDEX); - - if (!(svc = pa_dynarray_get(u->autoload_dynarray, index))) - return 0; - - if (!svc->autoload.valid) - return 0; - - svc->autoload.valid = 0; - pa_dynarray_put(u->autoload_dynarray, index, NULL); - - return publish_service(u, svc); -} - -static void subscribe_callback(pa_core *c, pa_subscription_event_type t, uint32_t index, void *userdata) { - struct userdata *u = userdata; - assert(u && c); - - switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) - case PA_SUBSCRIPTION_EVENT_SINK: { - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - pa_sink *sink; - - if ((sink = pa_idxset_get_by_index(c->sinks, index))) { - if (publish_sink(u, sink) < 0) - goto fail; - } - } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - if (remove_sink(u, index) < 0) - goto fail; - } - - break; - - case PA_SUBSCRIPTION_EVENT_SOURCE: - - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - pa_source *source; - - if ((source = pa_idxset_get_by_index(c->sources, index))) { - if (publish_source(u, source) < 0) - goto fail; - } - } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - if (remove_source(u, index) < 0) - goto fail; - } - - break; - - case PA_SUBSCRIPTION_EVENT_AUTOLOAD: - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - pa_autoload_entry *autoload; - - if ((autoload = pa_idxset_get_by_index(c->autoload_idxset, index))) { - if (publish_autoload(u, autoload) < 0) - goto fail; - } - } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - if (remove_autoload(u, index) < 0) - goto fail; - } - - break; - } - - return; - -fail: - if (u->subscription) { - pa_subscription_free(u->subscription); - u->subscription = NULL; - } -} - -int pa__init(pa_core *c, pa_module*m) { - struct userdata *u; - uint32_t index, port = PA_NATIVE_DEFAULT_PORT; - pa_sink *sink; - pa_source *source; - pa_autoload_entry *autoload; - pa_modargs *ma = NULL; - char t[256], hn[256]; - int free_txt = 0; - sw_text_record txt; - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log(__FILE__": failed to parse module arguments.\n"); - goto fail; - } - - if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port == 0 || port >= 0xFFFF) { - pa_log(__FILE__": invalid port specified.\n"); - goto fail; - } - - m->userdata = u = pa_xmalloc(sizeof(struct userdata)); - u->core = c; - u->port = (uint16_t) port; - - if (!(u->howl_wrapper = pa_howl_wrapper_get(c))) - goto fail; - - u->services = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - u->sink_dynarray = pa_dynarray_new(); - u->source_dynarray = pa_dynarray_new(); - u->autoload_dynarray = pa_dynarray_new(); - - u->subscription = pa_subscription_new(c, - PA_SUBSCRIPTION_MASK_SINK| - PA_SUBSCRIPTION_MASK_SOURCE| - PA_SUBSCRIPTION_MASK_AUTOLOAD, subscribe_callback, u); - - for (sink = pa_idxset_first(c->sinks, &index); sink; sink = pa_idxset_next(c->sinks, &index)) - if (publish_sink(u, sink) < 0) - goto fail; - - for (source = pa_idxset_first(c->sources, &index); source; source = pa_idxset_next(c->sources, &index)) - if (publish_source(u, source) < 0) - goto fail; - - if (c->autoload_idxset) - for (autoload = pa_idxset_first(c->autoload_idxset, &index); autoload; autoload = pa_idxset_next(c->autoload_idxset, &index)) - if (publish_autoload(u, autoload) < 0) - goto fail; - - snprintf(t, sizeof(t), "Networked Audio Server on %s", pa_get_host_name(hn, sizeof(hn))); - - if (sw_text_record_init(&txt) != SW_OKAY) { - pa_log(__FILE__": sw_text_record_init() failed\n"); - goto fail; - } - free_txt = 1; - - txt_record_server_data(u->core, txt); - - if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t, - SERVICE_NAME_SERVER, - NULL, NULL, u->port, sw_text_record_bytes(txt), sw_text_record_len(txt), - publish_reply, u, &u->server_oid) != SW_OKAY) { - pa_log(__FILE__": failed to register server on zeroconf.\n"); - goto fail; - } - - sw_text_record_fina(txt); - pa_modargs_free(ma); - - return 0; - -fail: - pa__done(c, m); - - if (ma) - pa_modargs_free(ma); - - if (free_txt) - sw_text_record_fina(txt); - - return -1; -} - -static void service_free(void *p, void *userdata) { - struct service *s = p; - struct userdata *u = userdata; - assert(s && u); - sw_discovery_cancel(pa_howl_wrapper_get_discovery(u->howl_wrapper), s->oid); - pa_xfree(s->name); - pa_xfree(s); -} - -void pa__done(pa_core *c, pa_module*m) { - struct userdata*u; - assert(c && m); - - if (!(u = m->userdata)) - return; - - if (u->services) - pa_hashmap_free(u->services, service_free, u); - - if (u->sink_dynarray) - pa_dynarray_free(u->sink_dynarray, NULL, NULL); - if (u->source_dynarray) - pa_dynarray_free(u->source_dynarray, NULL, NULL); - if (u->autoload_dynarray) - pa_dynarray_free(u->autoload_dynarray, NULL, NULL); - - if (u->subscription) - pa_subscription_free(u->subscription); - - if (u->howl_wrapper) - pa_howl_wrapper_unref(u->howl_wrapper); - - - pa_xfree(u); -} - diff --git a/polyp/module.c b/polyp/module.c deleted file mode 100644 index 499ea299..00000000 --- a/polyp/module.c +++ /dev/null @@ -1,275 +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 -#endif - -#include -#include -#include -#include -#include -#include - -#include "module.h" -#include "xmalloc.h" -#include "subscribe.h" -#include "log.h" -#include "util.h" - -#define PA_SYMBOL_INIT "pa__init" -#define PA_SYMBOL_DONE "pa__done" - -#define UNLOAD_POLL_TIME 2 - -static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { - pa_core *c = userdata; - struct timeval ntv; - assert(c && c->mainloop == m && c->module_auto_unload_event == e); - - pa_module_unload_unused(c); - - pa_gettimeofday(&ntv); - ntv.tv_sec += UNLOAD_POLL_TIME; - m->time_restart(e, &ntv); -} - -pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { - pa_module *m = NULL; - int r; - - assert(c && name); - - if (c->disallow_module_loading) - goto fail; - - m = pa_xmalloc(sizeof(pa_module)); - - m->name = pa_xstrdup(name); - m->argument = pa_xstrdup(argument); - - if (!(m->dl = lt_dlopenext(name))) { - pa_log(__FILE__": Failed to open module \"%s\": %s\n", name, lt_dlerror()); - goto fail; - } - - if (!(m->init = (int (*)(pa_core *_c, pa_module*_m)) lt_dlsym(m->dl, PA_SYMBOL_INIT))) { - pa_log(__FILE__": Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.\n", name); - goto fail; - } - - if (!(m->done = (void (*)(pa_core *_c, pa_module*_m)) lt_dlsym(m->dl, PA_SYMBOL_DONE))) { - pa_log(__FILE__": Failed to load module \"%s\": symbol \""PA_SYMBOL_DONE"\" not found.\n", name); - goto fail; - } - - m->userdata = NULL; - m->core = c; - m->n_used = -1; - m->auto_unload = 0; - m->unload_requested = 0; - - assert(m->init); - if (m->init(c, m) < 0) { - pa_log_error(__FILE__": Failed to load module \"%s\" (argument: \"%s\"): initialization failed.\n", name, argument ? argument : ""); - goto fail; - } - - if (!c->modules) - c->modules = pa_idxset_new(NULL, NULL); - - if (!c->module_auto_unload_event) { - struct timeval ntv; - pa_gettimeofday(&ntv); - ntv.tv_sec += UNLOAD_POLL_TIME; - c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c); - } - assert(c->module_auto_unload_event); - - assert(c->modules); - r = pa_idxset_put(c->modules, m, &m->index); - assert(r >= 0 && m->index != PA_IDXSET_INVALID); - - pa_log_info(__FILE__": Loaded \"%s\" (index: #%u; argument: \"%s\").\n", m->name, m->index, m->argument ? m->argument : ""); - - pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index); - - return m; - -fail: - - if (m) { - pa_xfree(m->argument); - pa_xfree(m->name); - - if (m->dl) - lt_dlclose(m->dl); - - pa_xfree(m); - } - - return NULL; -} - -static void pa_module_free(pa_module *m) { - assert(m && m->done && m->core); - - if (m->core->disallow_module_loading) - return; - - pa_log_info(__FILE__": Unloading \"%s\" (index: #%u).\n", m->name, m->index); - - m->done(m->core, m); - - lt_dlclose(m->dl); - - pa_log_info(__FILE__": Unloaded \"%s\" (index: #%u).\n", m->name, m->index); - - pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_REMOVE, m->index); - - pa_xfree(m->name); - pa_xfree(m->argument); - pa_xfree(m); -} - -void pa_module_unload(pa_core *c, pa_module *m) { - assert(c && m); - - assert(c->modules); - if (!(m = pa_idxset_remove_by_data(c->modules, m, NULL))) - return; - - pa_module_free(m); -} - -void pa_module_unload_by_index(pa_core *c, uint32_t idx) { - pa_module *m; - assert(c && idx != PA_IDXSET_INVALID); - - assert(c->modules); - if (!(m = pa_idxset_remove_by_index(c->modules, idx))) - return; - - pa_module_free(m); -} - -static void free_callback(void *p, PA_GCC_UNUSED void *userdata) { - pa_module *m = p; - assert(m); - pa_module_free(m); -} - -void pa_module_unload_all(pa_core *c) { - assert(c); - - if (!c->modules) - return; - - pa_idxset_free(c->modules, free_callback, NULL); - c->modules = NULL; - - if (c->module_auto_unload_event) { - c->mainloop->time_free(c->module_auto_unload_event); - c->module_auto_unload_event = NULL; - } - - if (c->module_defer_unload_event) { - c->mainloop->defer_free(c->module_defer_unload_event); - c->module_defer_unload_event = NULL; - } -} - -static int unused_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void *userdata) { - pa_module *m = p; - time_t *now = userdata; - assert(p && del && now); - - if (m->n_used == 0 && m->auto_unload && m->last_used_time+m->core->module_idle_time <= *now) { - pa_module_free(m); - *del = 1; - } - - return 0; -} - -void pa_module_unload_unused(pa_core *c) { - time_t now; - assert(c); - - if (!c->modules) - return; - - time(&now); - pa_idxset_foreach(c->modules, unused_callback, &now); -} - -static int unload_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, PA_GCC_UNUSED void *userdata) { - pa_module *m = p; - assert(m); - - if (m->unload_requested) { - pa_module_free(m); - *del = 1; - } - - return 0; -} - -static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) { - pa_core *core = userdata; - api->defer_enable(e, 0); - - if (!core->modules) - return; - - pa_idxset_foreach(core->modules, unload_callback, NULL); - -} - -void pa_module_unload_request(pa_module *m) { - assert(m); - - m->unload_requested = 1; - - if (!m->core->module_defer_unload_event) - m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core); - - m->core->mainloop->defer_enable(m->core->module_defer_unload_event, 1); -} - -void pa_module_set_used(pa_module*m, int used) { - assert(m); - - if (m->n_used != used) - pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index); - - if (m->n_used != used && used == 0) - time(&m->last_used_time); - - m->n_used = used; -} - -pa_modinfo *pa_module_get_info(pa_module *m) { - assert(m); - - return pa_modinfo_get_by_handle(m->dl); -} diff --git a/polyp/module.h b/polyp/module.h deleted file mode 100644 index 6f137c15..00000000 --- a/polyp/module.h +++ /dev/null @@ -1,70 +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 -#include - -#include "core.h" -#include "modinfo.h" - -typedef struct pa_module pa_module; - -struct pa_module { - pa_core *core; - char *name, *argument; - uint32_t index; - - lt_dlhandle dl; - - int (*init)(pa_core *c, pa_module*m); - void (*done)(pa_core *c, pa_module*m); - - void *userdata; - - int n_used; - int auto_unload; - time_t last_used_time; - - int unload_requested; -}; - -pa_module* pa_module_load(pa_core *c, const char *name, const char*argument); -void pa_module_unload(pa_core *c, pa_module *m); -void pa_module_unload_by_index(pa_core *c, uint32_t idx); - -void pa_module_unload_all(pa_core *c); -void pa_module_unload_unused(pa_core *c); - -void pa_module_unload_request(pa_module *m); - -void pa_module_set_used(pa_module*m, int used); - -#define PA_MODULE_AUTHOR(s) const char * pa__get_author(void) { return s; } -#define PA_MODULE_DESCRIPTION(s) const char * pa__get_description(void) { return s; } -#define PA_MODULE_USAGE(s) const char * pa__get_usage(void) { return s; } -#define PA_MODULE_VERSION(s) const char * pa__get_version(void) { return s; } - -pa_modinfo *pa_module_get_info(pa_module *m); - -#endif diff --git a/polyp/namereg.c b/polyp/namereg.c deleted file mode 100644 index 07fb485c..00000000 --- a/polyp/namereg.c +++ /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 -#endif - -#include -#include -#include -#include -#include - -#include "namereg.h" -#include "autoload.h" -#include "source.h" -#include "sink.h" -#include "xmalloc.h" -#include "subscribe.h" -#include "util.h" - -struct namereg_entry { - pa_namereg_type_t type; - char *name; - void *data; -}; - -void pa_namereg_free(pa_core *c) { - assert(c); - if (!c->namereg) - return; - assert(pa_hashmap_size(c->namereg) == 0); - pa_hashmap_free(c->namereg, NULL, NULL); -} - -const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail) { - struct namereg_entry *e; - char *n = NULL; - int r; - - assert(c && name && data); - - if (!c->namereg) { - c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - assert(c->namereg); - } - - if ((e = pa_hashmap_get(c->namereg, name)) && fail) - return NULL; - - if (!e) - n = pa_xstrdup(name); - else { - unsigned i; - size_t l = strlen(name); - n = pa_xmalloc(l+3); - - for (i = 1; i <= 99; i++) { - snprintf(n, l+2, "%s%u", name, i); - - if (!(e = pa_hashmap_get(c->namereg, n))) - break; - } - - if (e) { - pa_xfree(n); - return NULL; - } - } - - assert(n); - e = pa_xmalloc(sizeof(struct namereg_entry)); - e->type = type; - e->name = n; - e->data = data; - - r = pa_hashmap_put(c->namereg, e->name, e); - assert (r >= 0); - - return e->name; - -} - -void pa_namereg_unregister(pa_core *c, const char *name) { - struct namereg_entry *e; - assert(c && name); - - e = pa_hashmap_remove(c->namereg, name); - assert(e); - - pa_xfree(e->name); - pa_xfree(e); -} - -void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload) { - struct namereg_entry *e; - uint32_t idx; - assert(c); - - if (!name) { - if (type == PA_NAMEREG_SOURCE) - name = pa_namereg_get_default_source_name(c); - else if (type == PA_NAMEREG_SINK) - name = pa_namereg_get_default_sink_name(c); - } - - if (!name) - return NULL; - - if (c->namereg && (e = pa_hashmap_get(c->namereg, name))) - if (e->type == e->type) - return e->data; - - if (pa_atou(name, &idx) < 0) { - - if (autoload) { - pa_autoload_request(c, name, type); - - if (c->namereg && (e = pa_hashmap_get(c->namereg, name))) - if (e->type == e->type) - return e->data; - } - - return NULL; - } - - if (type == PA_NAMEREG_SINK) - return pa_idxset_get_by_index(c->sinks, idx); - else if (type == PA_NAMEREG_SOURCE) - return pa_idxset_get_by_index(c->sources, idx); - else if (type == PA_NAMEREG_SAMPLE && c->scache) - return pa_idxset_get_by_index(c->scache, idx); - - return NULL; -} - -void pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type) { - char **s; - assert(c && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE)); - - s = type == PA_NAMEREG_SINK ? &c->default_sink_name : &c->default_source_name; - assert(s); - - if (!name && !*s) - return; - - if (name && *s && !strcmp(name, *s)) - return; - - pa_xfree(*s); - *s = pa_xstrdup(name); - pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX); -} - -const char *pa_namereg_get_default_sink_name(pa_core *c) { - pa_sink *s; - assert(c); - - if (c->default_sink_name) - return c->default_sink_name; - - if ((s = pa_idxset_first(c->sinks, NULL))) - pa_namereg_set_default(c, s->name, PA_NAMEREG_SINK); - - if (c->default_sink_name) - return c->default_sink_name; - - return NULL; -} - -const char *pa_namereg_get_default_source_name(pa_core *c) { - pa_source *s; - uint32_t idx; - - assert(c); - - if (c->default_source_name) - return c->default_source_name; - - for (s = pa_idxset_first(c->sources, &idx); s; s = pa_idxset_next(c->sources, &idx)) - if (!s->monitor_of) { - pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE); - break; - } - - if (!c->default_source_name) - if ((s = pa_idxset_first(c->sources, NULL))) - pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE); - - if (c->default_source_name) - return c->default_source_name; - - return NULL; -} diff --git a/polyp/namereg.h b/polyp/namereg.h deleted file mode 100644 index 961fd44b..00000000 --- a/polyp/namereg.h +++ /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" - -typedef enum pa_namereg_type { - PA_NAMEREG_SINK, - PA_NAMEREG_SOURCE, - PA_NAMEREG_SAMPLE -} pa_namereg_type_t; - -void pa_namereg_free(pa_core *c); - -const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail); -void pa_namereg_unregister(pa_core *c, const char *name); -void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload); -void pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type); - -const char *pa_namereg_get_default_sink_name(pa_core *c); -const char *pa_namereg_get_default_source_name(pa_core *c); - -#endif diff --git a/polyp/native-common.h b/polyp/native-common.h deleted file mode 100644 index 569f3b71..00000000 --- a/polyp/native-common.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef foonativecommonhfoo -#define foonativecommonhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "cdecl.h" -#include "polyplib-def.h" - -PA_C_DECL_BEGIN - -enum { - PA_COMMAND_ERROR, - PA_COMMAND_TIMEOUT, /* pseudo command */ - PA_COMMAND_REPLY, - PA_COMMAND_CREATE_PLAYBACK_STREAM, - PA_COMMAND_DELETE_PLAYBACK_STREAM, - PA_COMMAND_CREATE_RECORD_STREAM, - PA_COMMAND_DELETE_RECORD_STREAM, - PA_COMMAND_EXIT, - PA_COMMAND_REQUEST, - PA_COMMAND_AUTH, - PA_COMMAND_SET_CLIENT_NAME, - PA_COMMAND_LOOKUP_SINK, - PA_COMMAND_LOOKUP_SOURCE, - PA_COMMAND_DRAIN_PLAYBACK_STREAM, - PA_COMMAND_PLAYBACK_STREAM_KILLED, - PA_COMMAND_RECORD_STREAM_KILLED, - PA_COMMAND_STAT, - PA_COMMAND_GET_PLAYBACK_LATENCY, - PA_COMMAND_CREATE_UPLOAD_STREAM, - PA_COMMAND_DELETE_UPLOAD_STREAM, - PA_COMMAND_FINISH_UPLOAD_STREAM, - PA_COMMAND_PLAY_SAMPLE, - PA_COMMAND_REMOVE_SAMPLE, - - PA_COMMAND_GET_SERVER_INFO, - PA_COMMAND_GET_SINK_INFO, - PA_COMMAND_GET_SINK_INFO_LIST, - PA_COMMAND_GET_SOURCE_INFO, - PA_COMMAND_GET_SOURCE_INFO_LIST, - PA_COMMAND_GET_MODULE_INFO, - PA_COMMAND_GET_MODULE_INFO_LIST, - PA_COMMAND_GET_CLIENT_INFO, - PA_COMMAND_GET_CLIENT_INFO_LIST, - PA_COMMAND_GET_SINK_INPUT_INFO, - PA_COMMAND_GET_SINK_INPUT_INFO_LIST, - PA_COMMAND_GET_SOURCE_OUTPUT_INFO, - PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST, - PA_COMMAND_GET_SAMPLE_INFO, - PA_COMMAND_GET_SAMPLE_INFO_LIST, - PA_COMMAND_SUBSCRIBE, - PA_COMMAND_SUBSCRIBE_EVENT, - - PA_COMMAND_SET_SINK_VOLUME, - PA_COMMAND_SET_SINK_INPUT_VOLUME, - PA_COMMAND_CORK_PLAYBACK_STREAM, - PA_COMMAND_FLUSH_PLAYBACK_STREAM, - PA_COMMAND_TRIGGER_PLAYBACK_STREAM, - - PA_COMMAND_SET_DEFAULT_SINK, - PA_COMMAND_SET_DEFAULT_SOURCE, - - PA_COMMAND_SET_PLAYBACK_STREAM_NAME, - PA_COMMAND_SET_RECORD_STREAM_NAME, - - PA_COMMAND_KILL_CLIENT, - PA_COMMAND_KILL_SINK_INPUT, - PA_COMMAND_KILL_SOURCE_OUTPUT, - PA_COMMAND_LOAD_MODULE, - PA_COMMAND_UNLOAD_MODULE, - PA_COMMAND_ADD_AUTOLOAD, - PA_COMMAND_REMOVE_AUTOLOAD, - PA_COMMAND_GET_AUTOLOAD_INFO, - PA_COMMAND_GET_AUTOLOAD_INFO_LIST, - PA_COMMAND_GET_RECORD_LATENCY, - PA_COMMAND_CORK_RECORD_STREAM, - PA_COMMAND_FLUSH_RECORD_STREAM, - PA_COMMAND_PREBUF_PLAYBACK_STREAM, - PA_COMMAND_MAX -}; - -#define PA_NATIVE_COOKIE_LENGTH 256 -#define PA_NATIVE_COOKIE_FILE ".polypaudio-cookie" - -#define PA_NATIVE_DEFAULT_PORT 4713 - -#define PA_NATIVE_COOKIE_PROPERTY_NAME "protocol-native-cookie" -#define PA_NATIVE_SERVER_PROPERTY_NAME "protocol-native-server" - -#define PA_NATIVE_DEFAULT_UNIX_SOCKET "native" - - -PA_C_DECL_END - -#endif diff --git a/polyp/oss-util.c b/polyp/oss-util.c deleted file mode 100644 index ae6772fd..00000000 --- a/polyp/oss-util.c +++ /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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "oss-util.h" -#include "util.h" -#include "log.h" - -int pa_oss_open(const char *device, int *mode, int* pcaps) { - int fd = -1; - assert(device && mode && (*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY)); - - if (*mode == O_RDWR) { - if ((fd = open(device, O_RDWR|O_NDELAY)) >= 0) { - int dcaps, *tcaps; - ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0); - - tcaps = pcaps ? pcaps : &dcaps; - - if (ioctl(fd, SNDCTL_DSP_GETCAPS, tcaps) < 0) { - pa_log(__FILE__": SNDCTL_DSP_GETCAPS: %s\n", strerror(errno)); - goto fail; - } - - if (*tcaps & DSP_CAP_DUPLEX) - return fd; - - goto fail; - } - - if ((fd = open(device, (*mode = O_WRONLY)|O_NDELAY)) < 0) { - if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY)) < 0) { - pa_log(__FILE__": open('%s'): %s\n", device, strerror(errno)); - goto fail; - } - } - } else { - if ((fd = open(device, *mode|O_NDELAY)) < 0) { - pa_log(__FILE__": open('%s'): %s\n", device, strerror(errno)); - goto fail; - } - } - - if (pcaps) { - if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) { - pa_log(__FILE__": SNDCTL_DSP_GETCAPS: %s\n", strerror(errno)); - goto fail; - } - } - - pa_fd_set_cloexec(fd, 1); - - return fd; - -fail: - if (fd >= 0) - close(fd); - return -1; -} - -int pa_oss_auto_format(int fd, pa_sample_spec *ss) { - int format, channels, speed, reqformat; - static const int format_trans[PA_SAMPLE_MAX] = { - [PA_SAMPLE_U8] = AFMT_U8, - [PA_SAMPLE_ALAW] = AFMT_A_LAW, - [PA_SAMPLE_ULAW] = AFMT_MU_LAW, - [PA_SAMPLE_S16LE] = AFMT_S16_LE, - [PA_SAMPLE_S16BE] = AFMT_S16_BE, - [PA_SAMPLE_FLOAT32LE] = AFMT_QUERY, /* not supported */ - [PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */ - }; - - assert(fd >= 0 && ss); - - reqformat = format = format_trans[ss->format]; - if (reqformat == AFMT_QUERY || ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != reqformat) { - format = AFMT_S16_NE; - if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) { - int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE; - format = f; - if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) { - format = AFMT_U8; - if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) { - pa_log(__FILE__": SNDCTL_DSP_SETFMT: %s\n", format != AFMT_U8 ? "No supported sample format" : strerror(errno)); - return -1; - } else - ss->format = PA_SAMPLE_U8; - } else - ss->format = f == AFMT_S16_LE ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE; - } else - ss->format = PA_SAMPLE_S16NE; - } - - channels = ss->channels; - if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) { - pa_log(__FILE__": SNDCTL_DSP_CHANNELS: %s\n", strerror(errno)); - return -1; - } - assert(channels); - ss->channels = channels; - - speed = ss->rate; - if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) { - pa_log(__FILE__": SNDCTL_DSP_SPEED: %s\n", strerror(errno)); - return -1; - } - assert(speed); - ss->rate = speed; - - return 0; -} - -static int simple_log2(int v) { - int k = 0; - - for (;;) { - v >>= 1; - if (!v) break; - k++; - } - - return k; -} - -int pa_oss_set_fragments(int fd, int nfrags, int frag_size) { - int arg; - arg = ((int) nfrags << 16) | simple_log2(frag_size); - - if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) { - pa_log(__FILE__": SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno)); - return -1; - } - - return 0; -} diff --git a/polyp/oss-util.h b/polyp/oss-util.h deleted file mode 100644 index 3ee51cc5..00000000 --- a/polyp/oss-util.h +++ /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, pa_sample_spec *ss); - -int pa_oss_set_fragments(int fd, int frags, int frag_size); - -#endif diff --git a/polyp/pabrowse.c b/polyp/pabrowse.c deleted file mode 100644 index 634c308a..00000000 --- a/polyp/pabrowse.c +++ /dev/null @@ -1,139 +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 -#endif - -#include -#include -#include - -#include -#include -#include -#include - -static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) { - fprintf(stderr, "Got signal, exiting\n"); - m->quit(m, 0); -} - -static void dump_server(const pa_browse_info *i) { - char t[16]; - - if (i->cookie) - snprintf(t, sizeof(t), "0x%08x", *i->cookie); - - printf("server: %s\n" - "server-version: %s\n" - "user-name: %s\n" - "fqdn: %s\n" - "cookie: %s\n", - i->server, - i->server_version ? i->server_version : "n/a", - i->user_name ? i->user_name : "n/a", - i->fqdn ? i->fqdn : "n/a", - i->cookie ? t : "n/a"); -} - -static void dump_device(const pa_browse_info *i) { - char t[16], ss[PA_SAMPLE_SPEC_SNPRINT_MAX]; - - if (i->sample_spec) - pa_sample_spec_snprint(ss, sizeof(ss), i->sample_spec); - - if (i->typeid) - pa_typeid_to_string(*i->typeid, t, sizeof(t)); - - printf("device: %s\n" - "description: %s\n" - "type: %s\n" - "sample spec: %s\n", - i->device, - i->description ? i->description : "n/a", - i->typeid ? t : "n/a", - i->sample_spec ? ss : "n/a"); - -} - -static void browser_callback(pa_browser *b, pa_browse_opcode c, const pa_browse_info *i, void *userdata) { - assert(b && i); - - switch (c) { - - case PA_BROWSE_NEW_SERVER: - printf("\n=> new server <%s>\n", i->name); - dump_server(i); - break; - - case PA_BROWSE_NEW_SINK: - printf("\n=> new sink <%s>\n", i->name); - dump_server(i); - dump_device(i); - break; - - case PA_BROWSE_NEW_SOURCE: - printf("\n=> new source <%s>\n", i->name); - dump_server(i); - dump_device(i); - break; - - case PA_BROWSE_REMOVE: - printf("\n=> removed service <%s>\n", i->name); - break; - - default: - ; - } -} - - -int main(int argc, char *argv[]) { - pa_mainloop *mainloop = NULL; - pa_browser *browser = NULL; - int ret = 1, r; - - if (!(mainloop = pa_mainloop_new())) - goto finish; - - r = pa_signal_init(pa_mainloop_get_api(mainloop)); - assert(r == 0); - pa_signal_new(SIGINT, exit_signal_callback, NULL); - pa_signal_new(SIGTERM, exit_signal_callback, NULL); - signal(SIGPIPE, SIG_IGN); - - if (!(browser = pa_browser_new(pa_mainloop_get_api(mainloop)))) - goto finish; - - pa_browser_set_callback(browser, browser_callback, NULL); - - ret = 0; - pa_mainloop_run(mainloop, &ret); - -finish: - if (mainloop) { - pa_signal_done(); - pa_mainloop_free(mainloop); - } - - return ret; -} diff --git a/polyp/pacat-simple.c b/polyp/pacat-simple.c deleted file mode 100644 index 2825fee5..00000000 --- a/polyp/pacat-simple.c +++ /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 -#endif - -#include -#include -#include -#include - -#include -#include -#include "gccmacro.h" - -#define BUFSIZE 1024 - -int main(PA_GCC_UNUSED int argc, char*argv[]) { - - /* The Sample format to use */ - static const pa_sample_spec ss = { - .format = PA_SAMPLE_S16LE, - .rate = 44100, - .channels = 2 - }; - - pa_simple *s = NULL; - int ret = 1; - int error; - - /* Create a new playback stream */ - if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, &error))) { - fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); - goto finish; - } - - for (;;) { - uint8_t buf[BUFSIZE]; - ssize_t r; - -#if 0 - pa_usec_t latency; - - if ((latency = pa_simple_get_playback_latency(s, &error)) == (pa_usec_t) -1) { - fprintf(stderr, __FILE__": pa_simple_get_playback_latency() failed: %s\n", pa_strerror(error)); - goto finish; - } - - fprintf(stderr, "%0.0f usec \r", (float)latency); -#endif - - /* Read some data ... */ - if ((r = read(STDIN_FILENO, buf, sizeof(buf))) <= 0) { - if (r == 0) /* EOF */ - break; - - fprintf(stderr, __FILE__": read() failed: %s\n", strerror(errno)); - goto finish; - } - - /* ... and play it */ - if (pa_simple_write(s, buf, r, &error) < 0) { - fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error)); - goto finish; - } - } - - /* Make sure that every single sample was played */ - if (pa_simple_drain(s, &error) < 0) { - fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error)); - goto finish; - } - - ret = 0; - -finish: - - if (s) - pa_simple_free(s); - - return ret; -} diff --git a/polyp/pacat.c b/polyp/pacat.c deleted file mode 100644 index bd2b64fd..00000000 --- a/polyp/pacat.c +++ /dev/null @@ -1,541 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#if PA_API_VERSION != 8 -#error Invalid Polypaudio API version -#endif - -static enum { RECORD, PLAYBACK } mode = PLAYBACK; - -static pa_context *context = NULL; -static pa_stream *stream = NULL; -static pa_mainloop_api *mainloop_api = NULL; - -static void *buffer = NULL; -static size_t buffer_length = 0, buffer_index = 0; - -static pa_io_event* stdio_event = NULL; - -static char *stream_name = NULL, *client_name = NULL, *device = NULL; - -static int verbose = 0; -static pa_volume_t volume = PA_VOLUME_NORM; - -static pa_sample_spec sample_spec = { - .format = PA_SAMPLE_S16LE, - .rate = 44100, - .channels = 2 -}; - -/* A shortcut for terminating the application */ -static void quit(int ret) { - assert(mainloop_api); - mainloop_api->quit(mainloop_api, ret); -} - -/* Write some data to the stream */ -static void do_stream_write(size_t length) { - size_t l; - assert(length); - - if (!buffer || !buffer_length) - return; - - l = length; - if (l > buffer_length) - l = buffer_length; - - pa_stream_write(stream, (uint8_t*) buffer + buffer_index, l, NULL, 0); - buffer_length -= l; - buffer_index += l; - - if (!buffer_length) { - free(buffer); - buffer = NULL; - buffer_index = buffer_length = 0; - } -} - -/* This is called whenever new data may be written to the stream */ -static void stream_write_callback(pa_stream *s, size_t length, void *userdata) { - assert(s && length); - - if (stdio_event) - mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT); - - if (!buffer) - return; - - do_stream_write(length); -} - -/* This is called whenever new data may is available */ -static void stream_read_callback(pa_stream *s, const void*data, size_t length, void *userdata) { - assert(s && data && length); - - if (stdio_event) - mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT); - - if (buffer) { - fprintf(stderr, "Buffer overrun, dropping incoming data\n"); - return; - } - - buffer = malloc(buffer_length = length); - assert(buffer); - memcpy(buffer, data, length); - buffer_index = 0; -} - -/* This routine is called whenever the stream state changes */ -static void stream_state_callback(pa_stream *s, void *userdata) { - assert(s); - - switch (pa_stream_get_state(s)) { - case PA_STREAM_CREATING: - case PA_STREAM_TERMINATED: - break; - - case PA_STREAM_READY: - if (verbose) - fprintf(stderr, "Stream successfully created\n"); - break; - - case PA_STREAM_FAILED: - default: - fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s)))); - quit(1); - } -} - -/* This is called whenever the context status changes */ -static void context_state_callback(pa_context *c, void *userdata) { - assert(c); - - switch (pa_context_get_state(c)) { - case PA_CONTEXT_CONNECTING: - case PA_CONTEXT_AUTHORIZING: - case PA_CONTEXT_SETTING_NAME: - break; - - case PA_CONTEXT_READY: - - assert(c && !stream); - - if (verbose) - fprintf(stderr, "Connection established.\n"); - - stream = pa_stream_new(c, stream_name, &sample_spec, NULL); - assert(stream); - - pa_stream_set_state_callback(stream, stream_state_callback, NULL); - pa_stream_set_write_callback(stream, stream_write_callback, NULL); - pa_stream_set_read_callback(stream, stream_read_callback, NULL); - - if (mode == PLAYBACK) { - pa_cvolume cv; - pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, PA_CHANNELS_MAX, volume)); - } else - pa_stream_connect_record(stream, device, NULL, 0); - - break; - - case PA_CONTEXT_TERMINATED: - quit(0); - break; - - case PA_CONTEXT_FAILED: - default: - fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c))); - quit(1); - } -} - -/* Connection draining complete */ -static void context_drain_complete(pa_context*c, void *userdata) { - pa_context_disconnect(c); -} - -/* Stream draining complete */ -static void stream_drain_complete(pa_stream*s, int success, void *userdata) { - pa_operation *o; - - if (!success) { - fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context))); - quit(1); - } - - if (verbose) - fprintf(stderr, "Playback stream drained.\n"); - - pa_stream_disconnect(stream); - pa_stream_unref(stream); - stream = NULL; - - if (!(o = pa_context_drain(context, context_drain_complete, NULL))) - pa_context_disconnect(context); - else { - pa_operation_unref(o); - - if (verbose) - fprintf(stderr, "Draining connection to server.\n"); - } -} - -/* New data on STDIN **/ -static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) { - size_t l, w = 0; - ssize_t r; - assert(a == mainloop_api && e && stdio_event == e); - - if (buffer) { - mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL); - return; - } - - if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY || !(l = w = pa_stream_writable_size(stream))) - l = 4096; - - buffer = malloc(l); - assert(buffer); - if ((r = read(fd, buffer, l)) <= 0) { - if (r == 0) { - if (verbose) - fprintf(stderr, "Got EOF.\n"); - pa_operation_unref(pa_stream_drain(stream, stream_drain_complete, NULL)); - } else { - fprintf(stderr, "read() failed: %s\n", strerror(errno)); - quit(1); - } - - mainloop_api->io_free(stdio_event); - stdio_event = NULL; - return; - } - - buffer_length = r; - buffer_index = 0; - - if (w) - do_stream_write(w); -} - -/* Some data may be written to STDOUT */ -static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) { - ssize_t r; - assert(a == mainloop_api && e && stdio_event == e); - - if (!buffer) { - mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL); - return; - } - - assert(buffer_length); - - if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) { - fprintf(stderr, "write() failed: %s\n", strerror(errno)); - quit(1); - - mainloop_api->io_free(stdio_event); - stdio_event = NULL; - return; - } - - buffer_length -= r; - buffer_index += r; - - if (!buffer_length) { - free(buffer); - buffer = NULL; - buffer_length = buffer_index = 0; - } -} - -/* UNIX signal to quit recieved */ -static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) { - if (verbose) - fprintf(stderr, "Got signal, exiting.\n"); - quit(0); - -} - -/* Show the current latency */ -static void stream_get_latency_callback(pa_stream *s, const pa_latency_info *i, void *userdata) { - pa_usec_t total; - int negative = 0; - assert(s); - - if (!i) { - fprintf(stderr, "Failed to get latency: %s\n", pa_strerror(pa_context_errno(context))); - quit(1); - return; - } - - total = pa_stream_get_latency(s, i, &negative); - - fprintf(stderr, "Latency: buffer: %0.0f usec; sink: %0.0f usec; source: %0.0f usec; transport: %0.0f usec; total: %0.0f usec; synchronized clocks: %s.\n", - (float) i->buffer_usec, (float) i->sink_usec, (float) i->source_usec, (float) i->transport_usec, (float) total * (negative?-1:1), - i->synchronized_clocks ? "yes" : "no"); -} - -/* Someone requested that the latency is shown */ -static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) { - fprintf(stderr, "Got SIGUSR1, requesting latency.\n"); - pa_operation_unref(pa_stream_get_latency_info(stream, stream_get_latency_callback, NULL)); -} - - -static void help(const char *argv0) { - - printf("%s [options]\n\n" - " -h, --help Show this help\n" - " --version Show version\n\n" - " -r, --record Create a connection for recording\n" - " -p, --playback Create a connection for playback\n\n" - " -v, --verbose Enable verbose operations\n\n" - " -s, --server=SERVER The name of the server to connect to\n" - " -d, --device=DEVICE The name of the sink/source to connect to\n" - " -n, --client-name=NAME How to call this client on the server\n" - " --stream-name=NAME How to call this stream on the server\n" - " --volume=VOLUME Specify the initial (linear) volume in range 0...256\n" - " --rate=SAMPLERATE The sample rate in Hz (defaults to 44100)\n" - " --format=SAMPLEFORMAT The sample type, one of s16le, s16be, u8, float32le,\n" - " float32be, ulaw, alaw (defaults to s16ne)\n" - " --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n" - " (defaults to 2)\n", - argv0); -} - -enum { - ARG_VERSION = 256, - ARG_STREAM_NAME, - ARG_VOLUME, - ARG_SAMPLERATE, - ARG_SAMPLEFORMAT, - ARG_CHANNELS -}; - -int main(int argc, char *argv[]) { - pa_mainloop* m = NULL; - int ret = 1, r, c; - char *bn, *server = NULL; - - static const struct option long_options[] = { - {"record", 0, NULL, 'r'}, - {"playback", 0, NULL, 'p'}, - {"device", 1, NULL, 'd'}, - {"server", 1, NULL, 's'}, - {"client-name", 1, NULL, 'n'}, - {"stream-name", 1, NULL, ARG_STREAM_NAME}, - {"version", 0, NULL, ARG_VERSION}, - {"help", 0, NULL, 'h'}, - {"verbose", 0, NULL, 'v'}, - {"volume", 1, NULL, ARG_VOLUME}, - {"rate", 1, NULL, ARG_SAMPLERATE}, - {"format", 1, NULL, ARG_SAMPLEFORMAT}, - {"channels", 1, NULL, ARG_CHANNELS}, - {NULL, 0, NULL, 0} - }; - - if (!(bn = strrchr(argv[0], '/'))) - bn = argv[0]; - else - bn++; - - if (strstr(bn, "rec") || strstr(bn, "mon")) - mode = RECORD; - else if (strstr(bn, "cat") || strstr(bn, "play")) - mode = PLAYBACK; - - while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) { - - switch (c) { - case 'h' : - help(bn); - ret = 0; - goto quit; - - case ARG_VERSION: - printf("pacat "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version()); - ret = 0; - goto quit; - - case 'r': - mode = RECORD; - break; - - case 'p': - mode = PLAYBACK; - break; - - case 'd': - free(device); - device = strdup(optarg); - break; - - case 's': - free(server); - server = strdup(optarg); - break; - - case 'n': - free(client_name); - client_name = strdup(optarg); - break; - - case ARG_STREAM_NAME: - free(stream_name); - stream_name = strdup(optarg); - break; - - case 'v': - verbose = 1; - break; - - case ARG_VOLUME: { - int v = atoi(optarg); - volume = v < 0 ? 0 : v; - break; - } - - case ARG_CHANNELS: - sample_spec.channels = atoi(optarg); - break; - - case ARG_SAMPLEFORMAT: - sample_spec.format = pa_parse_sample_format(optarg); - break; - - case ARG_SAMPLERATE: - sample_spec.rate = atoi(optarg); - break; - - default: - goto quit; - } - } - - if (!client_name) - client_name = strdup(bn); - - if (!stream_name) - stream_name = strdup(client_name); - - if (!pa_sample_spec_valid(&sample_spec)) { - fprintf(stderr, "Invalid sample specification\n"); - goto quit; - } - - if (verbose) { - char t[PA_SAMPLE_SPEC_SNPRINT_MAX]; - pa_sample_spec_snprint(t, sizeof(t), &sample_spec); - fprintf(stderr, "Opening a %s stream with sample specification '%s'.\n", mode == RECORD ? "recording" : "playback", t); - } - - /* Set up a new main loop */ - if (!(m = pa_mainloop_new())) { - fprintf(stderr, "pa_mainloop_new() failed.\n"); - goto quit; - } - - mainloop_api = pa_mainloop_get_api(m); - - r = pa_signal_init(mainloop_api); - assert(r == 0); - pa_signal_new(SIGINT, exit_signal_callback, NULL); - pa_signal_new(SIGTERM, exit_signal_callback, NULL); -#ifdef SIGUSR1 - pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL); -#endif -#ifdef SIGPIPE - signal(SIGPIPE, SIG_IGN); -#endif - - if (!(stdio_event = mainloop_api->io_new(mainloop_api, - mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO, - mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT, - mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) { - fprintf(stderr, "source_io() failed.\n"); - goto quit; - } - - /* Create a new connection context */ - if (!(context = pa_context_new(mainloop_api, client_name))) { - fprintf(stderr, "pa_context_new() failed.\n"); - goto quit; - } - - pa_context_set_state_callback(context, context_state_callback, NULL); - - /* Connect the context */ - pa_context_connect(context, server, 1, NULL); - - /* Run the main loop */ - if (pa_mainloop_run(m, &ret) < 0) { - fprintf(stderr, "pa_mainloop_run() failed.\n"); - goto quit; - } - -quit: - if (stream) - pa_stream_unref(stream); - - if (context) - pa_context_unref(context); - - if (stdio_event) { - assert(mainloop_api); - mainloop_api->io_free(stdio_event); - } - - if (m) { - pa_signal_done(); - pa_mainloop_free(m); - } - - free(buffer); - - free(server); - free(device); - free(client_name); - free(stream_name); - - return ret; -} diff --git a/polyp/packet.c b/polyp/packet.c deleted file mode 100644 index b3a2e074..00000000 --- a/polyp/packet.c +++ /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 -#endif - -#include -#include - -#include "packet.h" -#include "xmalloc.h" - -pa_packet* pa_packet_new(size_t length) { - pa_packet *p; - assert(length); - p = pa_xmalloc(sizeof(pa_packet)+length); - p->ref = 1; - p->length = length; - p->data = (uint8_t*) (p+1); - p->type = PA_PACKET_APPENDED; - return p; -} - -pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length) { - pa_packet *p; - assert(data && length); - p = pa_xmalloc(sizeof(pa_packet)); - p->ref = 1; - p->length = length; - p->data = data; - p->type = PA_PACKET_DYNAMIC; - return p; -} - -pa_packet* pa_packet_ref(pa_packet *p) { - assert(p && p->ref >= 1); - p->ref++; - return p; -} - -void pa_packet_unref(pa_packet *p) { - assert(p && p->ref >= 1); - p->ref--; - - if (p->ref == 0) { - if (p->type == PA_PACKET_DYNAMIC) - pa_xfree(p->data); - pa_xfree(p); - } -} diff --git a/polyp/packet.h b/polyp/packet.h deleted file mode 100644 index 0ac47485..00000000 --- a/polyp/packet.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef foopackethfoo -#define foopackethfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include - -typedef struct pa_packet { - enum { PA_PACKET_APPENDED, PA_PACKET_DYNAMIC } type; - unsigned ref; - size_t length; - uint8_t *data; -} pa_packet; - -pa_packet* pa_packet_new(size_t length); -pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length); - -pa_packet* pa_packet_ref(pa_packet *p); -void pa_packet_unref(pa_packet *p); - -#endif diff --git a/polyp/pacmd.c b/polyp/pacmd.c deleted file mode 100644 index e6c0da6a..00000000 --- a/polyp/pacmd.c +++ /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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "util.h" -#include "log.h" -#include "pid.h" - -int main(PA_GCC_UNUSED int main, PA_GCC_UNUSED char*argv[]) { - pid_t pid ; - int fd = -1; - int ret = 1, i; - struct sockaddr_un sa; - char ibuf[256], obuf[256]; - size_t ibuf_index, ibuf_length, obuf_index, obuf_length; - fd_set ifds, ofds; - - if (pa_pid_file_check_running(&pid) < 0) { - pa_log(__FILE__": no Polypaudio daemon running\n"); - goto fail; - } - - if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { - pa_log(__FILE__": socket(PF_UNIX, SOCK_STREAM, 0): %s\n", strerror(errno)); - goto fail; - } - - memset(&sa, 0, sizeof(sa)); - sa.sun_family = AF_UNIX; - pa_runtime_path("cli", sa.sun_path, sizeof(sa.sun_path)); - - for (i = 0; i < 5; i++) { - int r; - - if ((r = connect(fd, (struct sockaddr*) &sa, sizeof(sa))) < 0 && (errno != ECONNREFUSED && errno != ENOENT)) { - pa_log(__FILE__": connect() failed: %s\n", strerror(errno)); - goto fail; - } - - if (r >= 0) - break; - - if (pa_pid_file_kill(SIGUSR2, NULL) < 0) { - pa_log(__FILE__": failed to kill Polypaudio daemon.\n"); - goto fail; - } - - pa_msleep(50); - } - - if (i >= 5) { - pa_log(__FILE__": daemon not responding.\n"); - goto fail; - } - - ibuf_index = ibuf_length = obuf_index = obuf_length = 0; - - - FD_ZERO(&ifds); - FD_SET(0, &ifds); - FD_SET(fd, &ifds); - - FD_ZERO(&ofds); - - for (;;) { - if (select(FD_SETSIZE, &ifds, &ofds, NULL, NULL) < 0) { - pa_log(__FILE__": select() failed: %s\n", strerror(errno)); - goto fail; - } - - if (FD_ISSET(0, &ifds)) { - ssize_t r; - assert(!ibuf_length); - - if ((r = read(0, ibuf, sizeof(ibuf))) <= 0) { - if (r == 0) - break; - - pa_log(__FILE__": read() failed: %s\n", strerror(errno)); - goto fail; - } - - ibuf_length = (size_t) r; - ibuf_index = 0; - } - - if (FD_ISSET(fd, &ifds)) { - ssize_t r; - assert(!obuf_length); - - if ((r = read(fd, obuf, sizeof(obuf))) <= 0) { - if (r == 0) - break; - - pa_log(__FILE__": read() failed: %s\n", strerror(errno)); - goto fail; - } - - obuf_length = (size_t) r; - obuf_index = 0; - } - - if (FD_ISSET(1, &ofds)) { - ssize_t r; - assert(obuf_length); - - if ((r = write(1, obuf + obuf_index, obuf_length)) < 0) { - pa_log(__FILE__": write() failed: %s\n", strerror(errno)); - goto fail; - } - - obuf_length -= (size_t) r; - obuf_index += obuf_index; - - } - - if (FD_ISSET(fd, &ofds)) { - ssize_t r; - assert(ibuf_length); - - if ((r = write(fd, ibuf + ibuf_index, ibuf_length)) < 0) { - pa_log(__FILE__": write() failed: %s\n", strerror(errno)); - goto fail; - } - - ibuf_length -= (size_t) r; - ibuf_index += obuf_index; - - } - - FD_ZERO(&ifds); - FD_ZERO(&ofds); - - if (obuf_length <= 0) - FD_SET(fd, &ifds); - else - FD_SET(1, &ofds); - - if (ibuf_length <= 0) - FD_SET(0, &ifds); - else - FD_SET(fd, &ofds); - } - - - ret = 0; - -fail: - if (fd >= 0) - close(fd); - - return ret; -} diff --git a/polyp/pactl.c b/polyp/pactl.c deleted file mode 100644 index 23bd924b..00000000 --- a/polyp/pactl.c +++ /dev/null @@ -1,784 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#if PA_API_VERSION != 8 -#error Invalid Polypaudio API version -#endif - -#define BUFSIZE 1024 - -static pa_context *context = NULL; -static pa_mainloop_api *mainloop_api = NULL; - -static char *device = NULL, *sample_name = NULL; - -static SNDFILE *sndfile = NULL; -static pa_stream *sample_stream = NULL; -static pa_sample_spec sample_spec; -static size_t sample_length = 0; - -static int actions = 1; - -static int nl = 0; - -static enum { - NONE, - EXIT, - STAT, - UPLOAD_SAMPLE, - PLAY_SAMPLE, - REMOVE_SAMPLE, - LIST -} action = NONE; - -static void quit(int ret) { - assert(mainloop_api); - mainloop_api->quit(mainloop_api, ret); -} - - -static void context_drain_complete(pa_context *c, void *userdata) { - pa_context_disconnect(c); -} - -static void drain(void) { - pa_operation *o; - if (!(o = pa_context_drain(context, context_drain_complete, NULL))) - pa_context_disconnect(context); - else - pa_operation_unref(o); -} - - -static void complete_action(void) { - assert(actions > 0); - - if (!(--actions)) - drain(); -} - -static void stat_callback(pa_context *c, const pa_stat_info *i, void *userdata) { - char s[128]; - if (!i) { - fprintf(stderr, "Failed to get statistics: %s\n", pa_strerror(pa_context_errno(c))); - quit(1); - return; - } - - pa_bytes_snprint(s, sizeof(s), i->memblock_total_size); - printf("Currently in use: %u blocks containing %s bytes total.\n", i->memblock_total, s); - - pa_bytes_snprint(s, sizeof(s), i->memblock_allocated_size); - printf("Allocated during whole lifetime: %u blocks containing %s bytes total.\n", i->memblock_allocated, s); - - pa_bytes_snprint(s, sizeof(s), i->scache_size); - printf("Sample cache size: %s\n", s); - - complete_action(); -} - -static void get_server_info_callback(pa_context *c, const pa_server_info *i, void *useerdata) { - char s[PA_SAMPLE_SPEC_SNPRINT_MAX]; - - if (!i) { - fprintf(stderr, "Failed to get server information: %s\n", pa_strerror(pa_context_errno(c))); - quit(1); - return; - } - - pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec); - - printf("User name: %s\n" - "Host Name: %s\n" - "Server Name: %s\n" - "Server Version: %s\n" - "Default Sample Specification: %s\n" - "Default Sink: %s\n" - "Default Source: %s\n" - "Cookie: %08x\n", - i->user_name, - i->host_name, - i->server_name, - i->server_version, - s, - i->default_sink_name, - i->default_source_name, - i->cookie); - - complete_action(); -} - -static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) { - char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; - - if (is_last < 0) { - fprintf(stderr, "Failed to get sink information: %s\n", pa_strerror(pa_context_errno(c))); - quit(1); - return; - } - - if (is_last) { - complete_action(); - return; - } - - assert(i); - - if (nl) - printf("\n"); - nl = 1; - - printf("*** Sink #%u ***\n" - "Name: %s\n" - "Driver: %s\n" - "Description: %s\n" - "Sample Specification: %s\n" - "Channel Map: %s\n" - "Owner Module: %u\n" - "Volume: %s\n" - "Monitor Source: %u\n" - "Latency: %0.0f usec\n", - i->index, - i->name, - i->driver, - i->description, - pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec), - pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), - i->owner_module, - pa_cvolume_snprint(cv, sizeof(cv), &i->volume), - i->monitor_source, - (double) i->latency); - -} - -static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) { - char s[PA_SAMPLE_SPEC_SNPRINT_MAX], t[32], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; - - if (is_last < 0) { - fprintf(stderr, "Failed to get source information: %s\n", pa_strerror(pa_context_errno(c))); - quit(1); - return; - } - - if (is_last) { - complete_action(); - return; - } - - assert(i); - - if (nl) - printf("\n"); - nl = 1; - - snprintf(t, sizeof(t), "%u", i->monitor_of_sink); - - printf("*** Source #%u ***\n" - "Name: %s\n" - "Driver: %s\n" - "Description: %s\n" - "Sample Specification: %s\n" - "Channel Map: %s\n" - "Owner Module: %u\n" - "Monitor of Sink: %s\n" - "Latency: %0.0f usec\n", - i->index, - i->driver, - i->name, - i->description, - pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec), - pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), - i->owner_module, - i->monitor_of_sink != PA_INVALID_INDEX ? t : "no", - (double) i->latency); - -} - -static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) { - char t[32]; - - if (is_last < 0) { - fprintf(stderr, "Failed to get module information: %s\n", pa_strerror(pa_context_errno(c))); - quit(1); - return; - } - - if (is_last) { - complete_action(); - return; - } - - assert(i); - - if (nl) - printf("\n"); - nl = 1; - - snprintf(t, sizeof(t), "%u", i->n_used); - - printf("*** Module #%u ***\n" - "Name: %s\n" - "Argument: %s\n" - "Usage counter: %s\n" - "Auto unload: %s\n", - i->index, - i->name, - i->argument, - i->n_used != PA_INVALID_INDEX ? t : "n/a", - i->auto_unload ? "yes" : "no"); -} - -static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) { - char t[32]; - - if (is_last < 0) { - fprintf(stderr, "Failed to get client information: %s\n", pa_strerror(pa_context_errno(c))); - quit(1); - return; - } - - if (is_last) { - complete_action(); - return; - } - - assert(i); - - if (nl) - printf("\n"); - nl = 1; - - snprintf(t, sizeof(t), "%u", i->owner_module); - - printf("*** Client #%u ***\n" - "Name: %s\n" - "Driver: %s\n" - "Owner Module: %s\n", - i->index, - i->name, - i->driver, - i->owner_module != PA_INVALID_INDEX ? t : "n/a"); -} - -static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) { - char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; - - if (is_last < 0) { - fprintf(stderr, "Failed to get sink input information: %s\n", pa_strerror(pa_context_errno(c))); - quit(1); - return; - } - - if (is_last) { - complete_action(); - return; - } - - assert(i); - - if (nl) - printf("\n"); - nl = 1; - - snprintf(t, sizeof(t), "%u", i->owner_module); - snprintf(k, sizeof(k), "%u", i->client); - - printf("*** Sink Input #%u ***\n" - "Name: %s\n" - "Driver: %s\n" - "Owner Module: %s\n" - "Client: %s\n" - "Sink: %u\n" - "Sample Specification: %s\n" - "Channel Map: %s\n" - "Volume: %s\n" - "Buffer Latency: %0.0f usec\n" - "Sink Latency: %0.0f usec\n" - "Resample method: %s\n", - i->index, - i->name, - i->driver, - i->owner_module != PA_INVALID_INDEX ? t : "n/a", - i->client != PA_INVALID_INDEX ? k : "n/a", - i->sink, - pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec), - pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), - pa_cvolume_snprint(cv, sizeof(cv), &i->volume), - (double) i->buffer_usec, - (double) i->sink_usec, - i->resample_method ? i->resample_method : "n/a"); -} - - -static void get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int is_last, void *userdata) { - char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; - - if (is_last < 0) { - fprintf(stderr, "Failed to get source output information: %s\n", pa_strerror(pa_context_errno(c))); - quit(1); - return; - } - - if (is_last) { - complete_action(); - return; - } - - assert(i); - - if (nl) - printf("\n"); - nl = 1; - - - snprintf(t, sizeof(t), "%u", i->owner_module); - snprintf(k, sizeof(k), "%u", i->client); - - printf("*** Source Output #%u ***\n" - "Name: %s\n" - "Driver: %s\n" - "Owner Module: %s\n" - "Client: %s\n" - "Source: %u\n" - "Sample Specification: %s\n" - "Channel Map: %s\n" - "Buffer Latency: %0.0f usec\n" - "Source Latency: %0.0f usec\n" - "Resample method: %s\n", - i->index, - i->name, - i->driver, - i->owner_module != PA_INVALID_INDEX ? t : "n/a", - i->client != PA_INVALID_INDEX ? k : "n/a", - i->source, - pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec), - pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), - (double) i->buffer_usec, - (double) i->source_usec, - i->resample_method ? i->resample_method : "n/a"); -} - -static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) { - char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; - - if (is_last < 0) { - fprintf(stderr, "Failed to get sample information: %s\n", pa_strerror(pa_context_errno(c))); - quit(1); - return; - } - - if (is_last) { - complete_action(); - return; - } - - assert(i); - - if (nl) - printf("\n"); - nl = 1; - - - pa_bytes_snprint(t, sizeof(t), i->bytes); - - printf("*** Sample #%u ***\n" - "Name: %s\n" - "Volume: %s\n" - "Sample Specification: %s\n" - "Channel Map: %s\n" - "Duration: %0.1fs\n" - "Size: %s\n" - "Lazy: %s\n" - "Filename: %s\n", - i->index, - i->name, - pa_cvolume_snprint(cv, sizeof(cv), &i->volume), - pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : "n/a", - pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : "n/a", - (double) i->duration/1000000, - t, - i->lazy ? "yes" : "no", - i->filename ? i->filename : "n/a"); -} - -static void get_autoload_info_callback(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata) { - if (is_last < 0) { - fprintf(stderr, "Failed to get autoload information: %s\n", pa_strerror(pa_context_errno(c))); - quit(1); - return; - } - - if (is_last) { - complete_action(); - return; - } - - assert(i); - - if (nl) - printf("\n"); - nl = 1; - - printf("*** Autoload Entry #%u ***\n" - "Name: %s\n" - "Type: %s\n" - "Module: %s\n" - "Argument: %s\n", - i->index, - i->name, - i->type == PA_AUTOLOAD_SINK ? "sink" : "source", - i->module, - i->argument); -} - -static void simple_callback(pa_context *c, int success, void *userdata) { - if (!success) { - fprintf(stderr, "Failure: %s\n", pa_strerror(pa_context_errno(c))); - quit(1); - return; - } - - complete_action(); -} - -static void stream_state_callback(pa_stream *s, void *userdata) { - assert(s); - - switch (pa_stream_get_state(s)) { - case PA_STREAM_CREATING: - case PA_STREAM_READY: - break; - - case PA_STREAM_TERMINATED: - drain(); - break; - - case PA_STREAM_FAILED: - default: - fprintf(stderr, "Failed to upload sample: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s)))); - quit(1); - } -} - -static void stream_write_callback(pa_stream *s, size_t length, void *userdata) { - sf_count_t l; - float *d; - assert(s && length && sndfile); - - d = malloc(length); - assert(d); - - assert(sample_length >= length); - l = length/pa_frame_size(&sample_spec); - - if ((sf_readf_float(sndfile, d, l)) != l) { - free(d); - fprintf(stderr, "Premature end of file\n"); - quit(1); - } - - pa_stream_write(s, d, length, free, 0); - - sample_length -= length; - - if (sample_length <= 0) { - pa_stream_set_write_callback(sample_stream, NULL, NULL); - pa_stream_finish_upload(sample_stream); - } -} - -static void context_state_callback(pa_context *c, void *userdata) { - assert(c); - switch (pa_context_get_state(c)) { - case PA_CONTEXT_CONNECTING: - case PA_CONTEXT_AUTHORIZING: - case PA_CONTEXT_SETTING_NAME: - break; - - case PA_CONTEXT_READY: - switch (action) { - case STAT: - actions = 2; - pa_operation_unref(pa_context_stat(c, stat_callback, NULL)); - pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL)); - break; - - case PLAY_SAMPLE: - pa_operation_unref(pa_context_play_sample(c, sample_name, device, PA_VOLUME_NORM, simple_callback, NULL)); - break; - - case REMOVE_SAMPLE: - pa_operation_unref(pa_context_remove_sample(c, sample_name, simple_callback, NULL)); - break; - - case UPLOAD_SAMPLE: - sample_stream = pa_stream_new(c, sample_name, &sample_spec, NULL); - assert(sample_stream); - - pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL); - pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL); - pa_stream_connect_upload(sample_stream, sample_length); - break; - - case EXIT: - pa_context_exit_daemon(c); - drain(); - - case LIST: - actions = 8; - pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL)); - pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL)); - pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL)); - pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL)); - pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL)); - pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL)); - pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL)); - pa_operation_unref(pa_context_get_autoload_info_list(c, get_autoload_info_callback, NULL)); - break; - - default: - assert(0); - } - break; - - case PA_CONTEXT_TERMINATED: - quit(0); - break; - - case PA_CONTEXT_FAILED: - default: - fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c))); - quit(1); - } -} - -static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) { - fprintf(stderr, "Got SIGINT, exiting.\n"); - quit(0); -} - -static void help(const char *argv0) { - - printf("%s [options] stat\n" - "%s [options] list\n" - "%s [options] exit\n" - "%s [options] upload-sample FILENAME [NAME]\n" - "%s [options] play-sample NAME [SINK]\n" - "%s [options] remove-sample NAME\n\n" - " -h, --help Show this help\n" - " --version Show version\n\n" - " -s, --server=SERVER The name of the server to connect to\n" - " -n, --client-name=NAME How to call this client on the server\n", - argv0, argv0, argv0, argv0, argv0, argv0); -} - -enum { ARG_VERSION = 256 }; - -int main(int argc, char *argv[]) { - pa_mainloop* m = NULL; - char tmp[PATH_MAX]; - int ret = 1, r, c; - char *server = NULL, *client_name = NULL, *bn; - - static const struct option long_options[] = { - {"server", 1, NULL, 's'}, - {"client-name", 1, NULL, 'n'}, - {"version", 0, NULL, ARG_VERSION}, - {"help", 0, NULL, 'h'}, - {NULL, 0, NULL, 0} - }; - - if (!(bn = strrchr(argv[0], '/'))) - bn = argv[0]; - else - bn++; - - while ((c = getopt_long(argc, argv, "s:n:h", long_options, NULL)) != -1) { - switch (c) { - case 'h' : - help(bn); - ret = 0; - goto quit; - - case ARG_VERSION: - printf("pactl "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version()); - ret = 0; - goto quit; - - case 's': - free(server); - server = strdup(optarg); - break; - - case 'n': - free(client_name); - client_name = strdup(optarg); - break; - - default: - goto quit; - } - } - - if (!client_name) - client_name = strdup(bn); - - if (optind < argc) { - if (!strcmp(argv[optind], "stat")) - action = STAT; - else if (!strcmp(argv[optind], "exit")) - action = EXIT; - else if (!strcmp(argv[optind], "list")) - action = LIST; - else if (!strcmp(argv[optind], "upload-sample")) { - struct SF_INFO sfinfo; - action = UPLOAD_SAMPLE; - - if (optind+1 >= argc) { - fprintf(stderr, "Please specify a sample file to load\n"); - goto quit; - } - - if (optind+2 < argc) - sample_name = strdup(argv[optind+2]); - else { - char *f = strrchr(argv[optind+1], '/'); - size_t n; - if (f) - f++; - else - f = argv[optind]; - - n = strcspn(f, "."); - strncpy(tmp, f, n); - tmp[n] = 0; - sample_name = strdup(tmp); - } - - memset(&sfinfo, 0, sizeof(sfinfo)); - if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfinfo))) { - fprintf(stderr, "Failed to open sound file.\n"); - goto quit; - } - - sample_spec.format = PA_SAMPLE_FLOAT32; - sample_spec.rate = sfinfo.samplerate; - sample_spec.channels = sfinfo.channels; - - sample_length = sfinfo.frames*pa_frame_size(&sample_spec); - } else if (!strcmp(argv[optind], "play-sample")) { - action = PLAY_SAMPLE; - if (optind+1 >= argc) { - fprintf(stderr, "You have to specify a sample name to play\n"); - goto quit; - } - - sample_name = strdup(argv[optind+1]); - - if (optind+2 < argc) - device = strdup(argv[optind+2]); - - } else if (!strcmp(argv[optind], "remove-sample")) { - action = REMOVE_SAMPLE; - if (optind+1 >= argc) { - fprintf(stderr, "You have to specify a sample name to remove\n"); - goto quit; - } - - sample_name = strdup(argv[optind+1]); - } - } - - if (action == NONE) { - fprintf(stderr, "No valid command specified.\n"); - goto quit; - } - - if (!(m = pa_mainloop_new())) { - fprintf(stderr, "pa_mainloop_new() failed.\n"); - goto quit; - } - - mainloop_api = pa_mainloop_get_api(m); - - r = pa_signal_init(mainloop_api); - assert(r == 0); - pa_signal_new(SIGINT, exit_signal_callback, NULL); -#ifdef SIGPIPE - signal(SIGPIPE, SIG_IGN); -#endif - - if (!(context = pa_context_new(mainloop_api, client_name))) { - fprintf(stderr, "pa_context_new() failed.\n"); - goto quit; - } - - pa_context_set_state_callback(context, context_state_callback, NULL); - pa_context_connect(context, server, 1, NULL); - - if (pa_mainloop_run(m, &ret) < 0) { - fprintf(stderr, "pa_mainloop_run() failed.\n"); - goto quit; - } - -quit: - if (sample_stream) - pa_stream_unref(sample_stream); - - if (context) - pa_context_unref(context); - - if (m) { - pa_signal_done(); - pa_mainloop_free(m); - } - - if (sndfile) - sf_close(sndfile); - - free(server); - free(device); - free(sample_name); - - return ret; -} diff --git a/polyp/paplay.c b/polyp/paplay.c deleted file mode 100644 index ddc1cbc1..00000000 --- a/polyp/paplay.c +++ /dev/null @@ -1,386 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#if PA_API_VERSION != 8 -#error Invalid Polypaudio API version -#endif - -static pa_context *context = NULL; -static pa_stream *stream = NULL; -static pa_mainloop_api *mainloop_api = NULL; - -static char *stream_name = NULL, *client_name = NULL, *device = NULL; - -static int verbose = 0; -static pa_volume_t volume = PA_VOLUME_NORM; - -static SNDFILE* sndfile = NULL; - -static pa_sample_spec sample_spec = { 0, 0, 0 }; - -static sf_count_t (*readf_function)(SNDFILE *_sndfile, void *ptr, sf_count_t frames); - -/* A shortcut for terminating the application */ -static void quit(int ret) { - assert(mainloop_api); - mainloop_api->quit(mainloop_api, ret); -} - -/* Connection draining complete */ -static void context_drain_complete(pa_context*c, void *userdata) { - pa_context_disconnect(c); -} - -/* Stream draining complete */ -static void stream_drain_complete(pa_stream*s, int success, void *userdata) { - pa_operation *o; - - if (!success) { - fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context))); - quit(1); - } - - if (verbose) - fprintf(stderr, "Playback stream drained.\n"); - - pa_stream_disconnect(stream); - pa_stream_unref(stream); - stream = NULL; - - if (!(o = pa_context_drain(context, context_drain_complete, NULL))) - pa_context_disconnect(context); - else { - pa_operation_unref(o); - - if (verbose) - fprintf(stderr, "Draining connection to server.\n"); - } -} - -/* This is called whenever new data may be written to the stream */ -static void stream_write_callback(pa_stream *s, size_t length, void *userdata) { - size_t k; - sf_count_t f, n; - void *data; - assert(s && length); - - if (!sndfile) - return; - - k = pa_frame_size(&sample_spec); - - data = malloc(length); - - n = length/k; - - f = readf_function(sndfile, data, n); - - if (f > 0) - pa_stream_write(s, data, f*k, free, 0); - - if (f < n) { - sf_close(sndfile); - sndfile = NULL; - pa_operation_unref(pa_stream_drain(s, stream_drain_complete, NULL)); - } -} - -/* This routine is called whenever the stream state changes */ -static void stream_state_callback(pa_stream *s, void *userdata) { - assert(s); - - switch (pa_stream_get_state(s)) { - case PA_STREAM_CREATING: - case PA_STREAM_TERMINATED: - break; - - case PA_STREAM_READY: - if (verbose) - fprintf(stderr, "Stream successfully created\n"); - break; - - case PA_STREAM_FAILED: - default: - fprintf(stderr, "Stream errror: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s)))); - quit(1); - } -} - -/* This is called whenever the context status changes */ -static void context_state_callback(pa_context *c, void *userdata) { - assert(c); - - switch (pa_context_get_state(c)) { - case PA_CONTEXT_CONNECTING: - case PA_CONTEXT_AUTHORIZING: - case PA_CONTEXT_SETTING_NAME: - break; - - case PA_CONTEXT_READY: { - pa_cvolume cv; - - assert(c && !stream); - - if (verbose) - fprintf(stderr, "Connection established.\n"); - - stream = pa_stream_new(c, stream_name, &sample_spec, NULL); - assert(stream); - - pa_stream_set_state_callback(stream, stream_state_callback, NULL); - pa_stream_set_write_callback(stream, stream_write_callback, NULL); - pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, PA_CHANNELS_MAX, volume)); - - break; - } - - case PA_CONTEXT_TERMINATED: - quit(0); - break; - - case PA_CONTEXT_FAILED: - default: - fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c))); - quit(1); - } -} - -/* UNIX signal to quit recieved */ -static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) { - if (verbose) - fprintf(stderr, "Got SIGINT, exiting.\n"); - quit(0); - -} - -static void help(const char *argv0) { - - printf("%s [options] [FILE]\n\n" - " -h, --help Show this help\n" - " --version Show version\n\n" - " -v, --verbose Enable verbose operations\n\n" - " -s, --server=SERVER The name of the server to connect to\n" - " -d, --device=DEVICE The name of the sink/source to connect to\n" - " -n, --client-name=NAME How to call this client on the server\n" - " --stream-name=NAME How to call this stream on the server\n" - " --volume=VOLUME Specify the initial (linear) volume in range 0...256\n", - argv0); -} - -enum { - ARG_VERSION = 256, - ARG_STREAM_NAME, - ARG_VOLUME -}; - -int main(int argc, char *argv[]) { - pa_mainloop* m = NULL; - int ret = 1, r, c; - char *bn, *server = NULL; - const char *filename; - SF_INFO sfinfo; - - static const struct option long_options[] = { - {"device", 1, NULL, 'd'}, - {"server", 1, NULL, 's'}, - {"client-name", 1, NULL, 'n'}, - {"stream-name", 1, NULL, ARG_STREAM_NAME}, - {"version", 0, NULL, ARG_VERSION}, - {"help", 0, NULL, 'h'}, - {"verbose", 0, NULL, 'v'}, - {"volume", 1, NULL, ARG_VOLUME}, - {NULL, 0, NULL, 0} - }; - - if (!(bn = strrchr(argv[0], '/'))) - bn = argv[0]; - else - bn++; - - while ((c = getopt_long(argc, argv, "d:s:n:hv", long_options, NULL)) != -1) { - - switch (c) { - case 'h' : - help(bn); - ret = 0; - goto quit; - - case ARG_VERSION: - printf("paplay "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version()); - ret = 0; - goto quit; - - case 'd': - free(device); - device = strdup(optarg); - break; - - case 's': - free(server); - server = strdup(optarg); - break; - - case 'n': - free(client_name); - client_name = strdup(optarg); - break; - - case ARG_STREAM_NAME: - free(stream_name); - stream_name = strdup(optarg); - break; - - case 'v': - verbose = 1; - break; - - case ARG_VOLUME: { - int v = atoi(optarg); - volume = v < 0 ? 0 : v; - break; - } - - default: - goto quit; - } - } - - - filename = optind < argc ? argv[optind] : "STDIN"; - - - if (!client_name) - client_name = strdup(bn); - - if (!stream_name) - stream_name = strdup(filename); - - memset(&sfinfo, 0, sizeof(sfinfo)); - - if (optind < argc) - sndfile = sf_open(filename, SFM_READ, &sfinfo); - else - sndfile = sf_open_fd(STDIN_FILENO, SFM_READ, &sfinfo, 0); - - if (!sndfile) { - fprintf(stderr, "Failed to open file '%s'\n", filename); - goto quit; - } - - sample_spec.rate = sfinfo.samplerate; - sample_spec.channels = sfinfo.channels; - - switch (sfinfo.format & 0xFF) { - case SF_FORMAT_PCM_16: - case SF_FORMAT_PCM_U8: - case SF_FORMAT_ULAW: - case SF_FORMAT_ALAW: - sample_spec.format = PA_SAMPLE_S16NE; - readf_function = (sf_count_t (*)(SNDFILE *_sndfile, void *ptr, sf_count_t frames)) sf_readf_short; - break; - case SF_FORMAT_FLOAT: - default: - sample_spec.format = PA_SAMPLE_FLOAT32NE; - readf_function = (sf_count_t (*)(SNDFILE *_sndfile, void *ptr, sf_count_t frames)) sf_readf_float; - break; - } - - if (verbose) { - char t[PA_SAMPLE_SPEC_SNPRINT_MAX]; - pa_sample_spec_snprint(t, sizeof(t), &sample_spec); - fprintf(stderr, "Using sample spec '%s'\n", t); - } - - /* Set up a new main loop */ - if (!(m = pa_mainloop_new())) { - fprintf(stderr, "pa_mainloop_new() failed.\n"); - goto quit; - } - - mainloop_api = pa_mainloop_get_api(m); - - r = pa_signal_init(mainloop_api); - assert(r == 0); - pa_signal_new(SIGINT, exit_signal_callback, NULL); -#ifdef SIGPIPE - signal(SIGPIPE, SIG_IGN); -#endif - - /* Create a new connection context */ - if (!(context = pa_context_new(mainloop_api, client_name))) { - fprintf(stderr, "pa_context_new() failed.\n"); - goto quit; - } - - pa_context_set_state_callback(context, context_state_callback, NULL); - - /* Connect the context */ - pa_context_connect(context, server, 1, NULL); - - /* Run the main loop */ - if (pa_mainloop_run(m, &ret) < 0) { - fprintf(stderr, "pa_mainloop_run() failed.\n"); - goto quit; - } - -quit: - if (stream) - pa_stream_unref(stream); - - if (context) - pa_context_unref(context); - - if (m) { - pa_signal_done(); - pa_mainloop_free(m); - } - - free(server); - free(device); - free(client_name); - free(stream_name); - - if (sndfile) - sf_close(sndfile); - - return ret; -} diff --git a/polyp/parec-simple.c b/polyp/parec-simple.c deleted file mode 100644 index 524cc4f1..00000000 --- a/polyp/parec-simple.c +++ /dev/null @@ -1,100 +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 -#endif - -#include -#include -#include -#include - -#include -#include -#include "gccmacro.h" - -#define BUFSIZE 1024 - -/* A simple routine calling UNIX write() in a loop */ -static ssize_t loop_write(int fd, const void*data, size_t size) { - ssize_t ret = 0; - - while (size > 0) { - ssize_t r; - - if ((r = write(fd, data, size)) < 0) - return r; - - if (r == 0) - break; - - ret += r; - data = (const uint8_t*) data + r; - size -= r; - } - - return ret; -} - -int main(PA_GCC_UNUSED int argc, char*argv[]) { - /* The sample type to use */ - static const pa_sample_spec ss = { - .format = PA_SAMPLE_S16LE, - .rate = 44100, - .channels = 2 - }; - pa_simple *s = NULL; - int ret = 1; - int error; - - /* Create the recording stream */ - if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "record", &ss, NULL, &error))) { - fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); - goto finish; - } - - for (;;) { - uint8_t buf[BUFSIZE]; - ssize_t r; - - /* Record some data ... */ - if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) { - fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error)); - goto finish; - } - - /* And write it to STDOUT */ - if ((r = loop_write(STDOUT_FILENO, buf, sizeof(buf))) <= 0) { - fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno)); - goto finish; - } - } - - ret = 0; - -finish: - - if (s) - pa_simple_free(s); - - return ret; -} diff --git a/polyp/parseaddr.c b/polyp/parseaddr.c deleted file mode 100644 index 5e4c689c..00000000 --- a/polyp/parseaddr.c +++ /dev/null @@ -1,112 +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 -#endif - -#include -#include -#include - -#include "xmalloc.h" -#include "util.h" -#include "parseaddr.h" - -/* Parse addresses in one of the following forms: - * HOSTNAME - * HOSTNAME:PORT - * [HOSTNAME] - * [HOSTNAME]:PORT - * - * Return a newly allocated string of the hostname and fill in *ret_port if specified */ - -static char *parse_host(const char *s, uint16_t *ret_port) { - assert(s && ret_port); - if (*s == '[') { - char *e; - if (!(e = strchr(s+1, ']'))) - return NULL; - - if (e[1] == ':') - *ret_port = atoi(e+2); - else if (e[1] != 0) - return NULL; - - return pa_xstrndup(s+1, e-s-1); - } else { - char *e; - - if (!(e = strrchr(s, ':'))) - return pa_xstrdup(s); - - *ret_port = atoi(e+1); - return pa_xstrndup(s, e-s); - } -} - -int pa_parse_address(const char *name, pa_parsed_address *ret_p) { - const char *p; - assert(name && ret_p); - memset(ret_p, 0, sizeof(pa_parsed_address)); - ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO; - - if (*name == '{') { - char hn[256], *pfx; - /* The URL starts with a host specification for detecting local connections */ - - if (!pa_get_host_name(hn, sizeof(hn))) - return -1; - - pfx = pa_sprintf_malloc("{%s}", hn); - if (!pa_startswith(name, pfx)) { - pa_xfree(pfx); - /* Not local */ - return -1; - } - - p = name + strlen(pfx); - pa_xfree(pfx); - } else - p = name; - - if (*p == '/') - ret_p->type = PA_PARSED_ADDRESS_UNIX; - else if (pa_startswith(p, "unix:")) { - ret_p->type = PA_PARSED_ADDRESS_UNIX; - p += sizeof("unix:")-1; - } else if (pa_startswith(p, "tcp:") || pa_startswith(p, "tcp4:")) { - ret_p->type = PA_PARSED_ADDRESS_TCP4; - p += sizeof("tcp:")-1; - } else if (pa_startswith(p, "tcp6:")) { - ret_p->type = PA_PARSED_ADDRESS_TCP6; - p += sizeof("tcp6:")-1; - } - - if (ret_p->type == PA_PARSED_ADDRESS_UNIX) - ret_p->path_or_host = pa_xstrdup(p); - else - if (!(ret_p->path_or_host = parse_host(p, &ret_p->port))) - return -1; - - - return 0; -} diff --git a/polyp/parseaddr.h b/polyp/parseaddr.h deleted file mode 100644 index eff9dd3b..00000000 --- a/polyp/parseaddr.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef fooparseaddrhfoo -#define fooparseaddrhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -typedef enum pa_parsed_address_type { - PA_PARSED_ADDRESS_UNIX, - PA_PARSED_ADDRESS_TCP4, - PA_PARSED_ADDRESS_TCP6, - PA_PARSED_ADDRESS_TCP_AUTO -} pa_parsed_address_type_t; - -typedef struct pa_parsed_address { - pa_parsed_address_type_t type; - char *path_or_host; - uint16_t port; -} pa_parsed_address; - -int pa_parse_address(const char *a, pa_parsed_address *ret_p); - -#endif diff --git a/polyp/pax11publish.c b/polyp/pax11publish.c deleted file mode 100644 index 42947836..00000000 --- a/polyp/pax11publish.c +++ /dev/null @@ -1,217 +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 -#endif - -#include -#include -#include -#include - -#include -#include - -#include "util.h" -#include "log.h" -#include "authkey.h" -#include "native-common.h" -#include "client-conf.h" -#include "x11prop.h" - -int main(int argc, char *argv[]) { - const char *dname = NULL, *sink = NULL, *source = NULL, *server = NULL, *cookie_file = PA_NATIVE_COOKIE_FILE; - int c, ret = 1; - Display *d = NULL; - enum { DUMP, EXPORT, IMPORT, REMOVE } mode = DUMP; - - while ((c = getopt(argc, argv, "deiD:S:O:I:c:hr")) != -1) { - switch (c) { - case 'D' : - dname = optarg; - break; - case 'h': - printf("%s [-D display] [-S server] [-O sink] [-I source] [-c file] [-d|-e|-i|-r]\n\n" - " -d Show current Polypaudio data attached to X11 display (default)\n" - " -e Export local Polypaudio data to X11 display\n" - " -i Import Polypaudio data from X11 display to local environment variables and cookie file.\n" - " -r Remove Polypaudio data from X11 display\n", - pa_path_get_filename(argv[0])); - ret = 0; - goto finish; - case 'd': - mode = DUMP; - break; - case 'e': - mode = EXPORT; - break; - case 'i': - mode = IMPORT; - break; - case 'r': - mode = REMOVE; - break; - case 'c': - cookie_file = optarg; - break; - case 'I': - source = optarg; - break; - case 'O': - sink = optarg; - break; - case 'S': - server = optarg; - break; - default: - fprintf(stderr, "Failed to parse command line.\n"); - goto finish; - } - } - - if (!(d = XOpenDisplay(dname))) { - pa_log(__FILE__": XOpenDisplay() failed\n"); - goto finish; - } - - switch (mode) { - case DUMP: { - char t[1024]; - if (pa_x11_get_prop(d, "POLYP_SERVER", t, sizeof(t))) - printf("Server: %s\n", t); - if (pa_x11_get_prop(d, "POLYP_SOURCE", t, sizeof(t))) - printf("Source: %s\n", t); - if (pa_x11_get_prop(d, "POLYP_SINK", t, sizeof(t))) - printf("Sink: %s\n", t); - if (pa_x11_get_prop(d, "POLYP_COOKIE", t, sizeof(t))) - printf("Cookie: %s\n", t); - - break; - } - - case IMPORT: { - char t[1024]; - if (pa_x11_get_prop(d, "POLYP_SERVER", t, sizeof(t))) - printf("POLYP_SERVER='%s'\nexport POLYP_SERVER\n", t); - if (pa_x11_get_prop(d, "POLYP_SOURCE", t, sizeof(t))) - printf("POLYP_SOURCE='%s'\nexport POLYP_SOURCE\n", t); - if (pa_x11_get_prop(d, "POLYP_SINK", t, sizeof(t))) - printf("POLYP_SINK='%s'\nexport POLYP_SINK\n", t); - - if (pa_x11_get_prop(d, "POLYP_COOKIE", t, sizeof(t))) { - uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; - size_t l; - if ((l = pa_parsehex(t, cookie, sizeof(cookie))) != sizeof(cookie)) { - fprintf(stderr, "Failed to parse cookie data\n"); - goto finish; - } - - if (pa_authkey_save(cookie_file, cookie, l) < 0) { - fprintf(stderr, "Failed to save cookie data\n"); - goto finish; - } - } - - break; - } - - case EXPORT: { - pa_client_conf *conf = pa_client_conf_new(); - uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; - char hx[PA_NATIVE_COOKIE_LENGTH*2+1]; - assert(conf); - - if (pa_client_conf_load(conf, NULL) < 0) { - fprintf(stderr, "Failed to load client configuration file.\n"); - goto finish; - } - - if (pa_client_conf_env(conf) < 0) { - fprintf(stderr, "Failed to read environment configuration data.\n"); - goto finish; - } - - pa_x11_del_prop(d, "POLYP_SERVER"); - pa_x11_del_prop(d, "POLYP_SINK"); - pa_x11_del_prop(d, "POLYP_SOURCE"); - pa_x11_del_prop(d, "POLYP_ID"); - pa_x11_del_prop(d, "POLYP_COOKIE"); - - if (server) - pa_x11_set_prop(d, "POLYP_SERVER", server); - else if (conf->default_server) - pa_x11_set_prop(d, "POLYP_SERVER", conf->default_server); - else { - char hn[256]; - if (!pa_get_fqdn(hn, sizeof(hn))) { - fprintf(stderr, "Failed to get FQDN.\n"); - goto finish; - } - - pa_x11_set_prop(d, "POLYP_SERVER", hn); - } - - if (sink) - pa_x11_set_prop(d, "POLYP_SINK", sink); - else if (conf->default_sink) - pa_x11_set_prop(d, "POLYP_SINK", conf->default_sink); - - if (source) - pa_x11_set_prop(d, "POLYP_SOURCE", source); - if (conf->default_source) - pa_x11_set_prop(d, "POLYP_SOURCE", conf->default_source); - - pa_client_conf_free(conf); - - if (pa_authkey_load_auto(cookie_file, cookie, sizeof(cookie)) < 0) { - fprintf(stderr, "Failed to load cookie data\n"); - goto finish; - } - - pa_x11_set_prop(d, "POLYP_COOKIE", pa_hexstr(cookie, sizeof(cookie), hx, sizeof(hx))); - break; - } - - case REMOVE: - pa_x11_del_prop(d, "POLYP_SERVER"); - pa_x11_del_prop(d, "POLYP_SINK"); - pa_x11_del_prop(d, "POLYP_SOURCE"); - pa_x11_del_prop(d, "POLYP_ID"); - pa_x11_del_prop(d, "POLYP_COOKIE"); - break; - - default: - fprintf(stderr, "No yet implemented.\n"); - goto finish; - } - - ret = 0; - -finish: - - if (d) { - XSync(d, False); - XCloseDisplay(d); - } - - return ret; -} diff --git a/polyp/pdispatch.c b/polyp/pdispatch.c deleted file mode 100644 index 5a50a0d5..00000000 --- a/polyp/pdispatch.c +++ /dev/null @@ -1,295 +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 -#endif - -#include -#include -#include - -#include "pdispatch.h" -#include "native-common.h" -#include "xmalloc.h" -#include "llist.h" -#include "log.h" -#include "util.h" - -/*#define DEBUG_OPCODES */ - -#ifdef DEBUG_OPCODES - -static const char *command_names[PA_COMMAND_MAX] = { - [PA_COMMAND_ERROR] = "ERROR", - [PA_COMMAND_TIMEOUT] = "TIMEOUT", - [PA_COMMAND_REPLY] = "REPLY", - [PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM", - [PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM", - [PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM", - [PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM", - [PA_COMMAND_AUTH] = "AUTH", - [PA_COMMAND_REQUEST] = "REQUEST", - [PA_COMMAND_EXIT] = "EXIT", - [PA_COMMAND_SET_CLIENT_NAME] = "SET_CLIENT_NAME", - [PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK", - [PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE", - [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM", - [PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED", - [PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED", - [PA_COMMAND_STAT] = "STAT", - [PA_COMMAND_GET_PLAYBACK_LATENCY] = "PLAYBACK_LATENCY", - [PA_COMMAND_CREATE_UPLOAD_STREAM] = "CREATE_UPLOAD_STREAM", - [PA_COMMAND_DELETE_UPLOAD_STREAM] = "DELETE_UPLOAD_STREAM", - [PA_COMMAND_FINISH_UPLOAD_STREAM] = "FINISH_UPLOAD_STREAM", - [PA_COMMAND_PLAY_SAMPLE] = "PLAY_SAMPLE", - [PA_COMMAND_REMOVE_SAMPLE] = "REMOVE_SAMPLE", - [PA_COMMAND_GET_SERVER_INFO] = "GET_SERVER_INFO", - [PA_COMMAND_GET_SINK_INFO] = "GET_SINK_INFO", - [PA_COMMAND_GET_SINK_INFO_LIST] = "GET_SINK_INFO_LIST", - [PA_COMMAND_GET_SOURCE_INFO] = "GET_SOURCE_INFO", - [PA_COMMAND_GET_SOURCE_INFO_LIST] = "GET_SOURCE_INFO_LIST", - [PA_COMMAND_GET_MODULE_INFO] = "GET_MODULE_INFO", - [PA_COMMAND_GET_MODULE_INFO_LIST] = "GET_MODULE_INFO_LIST", - [PA_COMMAND_GET_CLIENT_INFO] = "GET_CLIENT_INFO", - [PA_COMMAND_GET_CLIENT_INFO_LIST] = "GET_CLIENT_INFO_LIST", - [PA_COMMAND_GET_SAMPLE_INFO] = "GET_SAMPLE_INFO", - [PA_COMMAND_GET_SAMPLE_INFO_LIST] = "GET_SAMPLE_INFO_LIST", - [PA_COMMAND_GET_SINK_INPUT_INFO] = "GET_SINK_INPUT_INFO", - [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = "GET_SINK_INPUT_INFO_LIST", - [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = "GET_SOURCE_OUTPUT_INFO", - [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = "GET_SOURCE_OUTPUT_INFO_LIST", - [PA_COMMAND_SUBSCRIBE] = "SUBSCRIBE", - [PA_COMMAND_SUBSCRIBE_EVENT] = "SUBSCRIBE_EVENT", - [PA_COMMAND_SET_SINK_VOLUME] = "SET_SINK_VOLUME", - [PA_COMMAND_SET_SINK_INPUT_VOLUME] = "SET_SINK_INPUT_VOLUME", - [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = "TRIGGER_PLAYBACK_STREAM", - [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = "FLUSH_PLAYBACK_STREAM", - [PA_COMMAND_CORK_PLAYBACK_STREAM] = "CORK_PLAYBACK_STREAM", - [PA_COMMAND_GET_AUTOLOAD_INFO] = "GET_AUTOLOAD_INFO", - [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = "GET_AUTOLOAD_INFO_LIST", -}; - -#endif - -struct reply_info { - pa_pdispatch *pdispatch; - PA_LLIST_FIELDS(struct reply_info); - pa_pdispatch_callback callback; - void *userdata; - uint32_t tag; - pa_time_event *time_event; -}; - -struct pa_pdispatch { - int ref; - pa_mainloop_api *mainloop; - const pa_pdispatch_callback *callback_table; - unsigned n_commands; - PA_LLIST_HEAD(struct reply_info, replies); - pa_pdispatch_drain_callback drain_callback; - void *drain_userdata; -}; - -static void reply_info_free(struct reply_info *r) { - assert(r && r->pdispatch && r->pdispatch->mainloop); - - if (r->time_event) - r->pdispatch->mainloop->time_free(r->time_event); - - PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r); - - pa_xfree(r); -} - -pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_callback*table, unsigned entries) { - pa_pdispatch *pd; - assert(mainloop); - - assert((entries && table) || (!entries && !table)); - - pd = pa_xmalloc(sizeof(pa_pdispatch)); - pd->ref = 1; - pd->mainloop = mainloop; - pd->callback_table = table; - pd->n_commands = entries; - PA_LLIST_HEAD_INIT(pa_reply_info, pd->replies); - pd->drain_callback = NULL; - pd->drain_userdata = NULL; - - return pd; -} - -static void pdispatch_free(pa_pdispatch *pd) { - assert(pd); - - while (pd->replies) - reply_info_free(pd->replies); - - pa_xfree(pd); -} - -static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) { - pa_pdispatch_callback callback; - void *userdata; - uint32_t tag; - assert(r); - - pa_pdispatch_ref(pd); - - callback = r->callback; - userdata = r->userdata; - tag = r->tag; - - reply_info_free(r); - - callback(pd, command, tag, ts, userdata); - - if (pd->drain_callback && !pa_pdispatch_is_pending(pd)) - pd->drain_callback(pd, pd->drain_userdata); - - pa_pdispatch_unref(pd); -} - -int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, void *userdata) { - uint32_t tag, command; - pa_tagstruct *ts = NULL; - int ret = -1; - assert(pd && packet && packet->data); - - pa_pdispatch_ref(pd); - - if (packet->length <= 8) - goto finish; - - ts = pa_tagstruct_new(packet->data, packet->length); - assert(ts); - - if (pa_tagstruct_getu32(ts, &command) < 0 || - pa_tagstruct_getu32(ts, &tag) < 0) - goto finish; - -#ifdef DEBUG_OPCODES -{ - char t[256]; - char const *p; - if (!(p = command_names[command])) - snprintf((char*) (p = t), sizeof(t), "%u", command); - - pa_log(__FILE__": Recieved opcode <%s>\n", p); -} -#endif - - if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) { - struct reply_info *r; - - for (r = pd->replies; r; r = r->next) - if (r->tag == tag) - break; - - if (r) - run_action(pd, r, command, ts); - - } else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) { - const pa_pdispatch_callback *c = pd->callback_table+command; - - (*c)(pd, command, tag, ts, userdata); - } else { - pa_log(__FILE__": Recieved unsupported command %u\n", command); - goto finish; - } - - ret = 0; - -finish: - if (ts) - pa_tagstruct_free(ts); - - pa_pdispatch_unref(pd); - - return ret; -} - -static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { - struct reply_info*r = userdata; - assert(r && r->time_event == e && r->pdispatch && r->pdispatch->mainloop == m && r->callback); - - run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL); -} - -void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_callback cb, void *userdata) { - struct reply_info *r; - struct timeval tv; - assert(pd && pd->ref >= 1 && cb); - - r = pa_xmalloc(sizeof(struct reply_info)); - r->pdispatch = pd; - r->callback = cb; - r->userdata = userdata; - r->tag = tag; - - pa_gettimeofday(&tv); - tv.tv_sec += timeout; - - r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r); - assert(r->time_event); - - PA_LLIST_PREPEND(struct reply_info, pd->replies, r); -} - -int pa_pdispatch_is_pending(pa_pdispatch *pd) { - assert(pd); - - return !!pd->replies; -} - -void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) { - assert(pd); - assert(!cb || pa_pdispatch_is_pending(pd)); - - pd->drain_callback = cb; - pd->drain_userdata = userdata; -} - -void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) { - struct reply_info *r, *n; - assert(pd); - - for (r = pd->replies; r; r = n) { - n = r->next; - - if (r->userdata == userdata) - reply_info_free(r); - } -} - -void pa_pdispatch_unref(pa_pdispatch *pd) { - assert(pd && pd->ref >= 1); - - if (!(--(pd->ref))) - pdispatch_free(pd); -} - -pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) { - assert(pd && pd->ref >= 1); - pd->ref++; - return pd; -} diff --git a/polyp/pdispatch.h b/polyp/pdispatch.h deleted file mode 100644 index 40f5d4c4..00000000 --- a/polyp/pdispatch.h +++ /dev/null @@ -1,50 +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 -#include "tagstruct.h" -#include "packet.h" -#include "mainloop-api.h" - -typedef struct pa_pdispatch pa_pdispatch; - -typedef void (*pa_pdispatch_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); - -pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, const pa_pdispatch_callback*table, unsigned entries); -void pa_pdispatch_unref(pa_pdispatch *pd); -pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd); - -int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*p, void *userdata); - -void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_callback callback, void *userdata); - -int pa_pdispatch_is_pending(pa_pdispatch *pd); - -typedef void (*pa_pdispatch_drain_callback)(pa_pdispatch *pd, void *userdata); -void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_callback callback, void *userdata); - -/* Remove all reply slots with the give userdata parameter */ -void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata); - -#endif diff --git a/polyp/pid.c b/polyp/pid.c deleted file mode 100644 index ae3dc7f5..00000000 --- a/polyp/pid.c +++ /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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_WINDOWS_H -#include -#endif - -#include "pid.h" -#include "util.h" -#include "log.h" - -/* Read the PID data from the file descriptor fd, and return it. If no - * pid could be read, return 0, on failure (pid_t) -1 */ -static pid_t read_pid(const char *fn, int fd) { - ssize_t r; - char t[20], *e; - uint32_t pid; - - assert(fn && fd >= 0); - - if ((r = pa_loop_read(fd, t, sizeof(t)-1)) < 0) { - pa_log(__FILE__": WARNING: failed to read PID file '%s': %s\n", fn, strerror(errno)); - return (pid_t) -1; - } - - if (r == 0) - return (pid_t) 0; - - t[r] = 0; - if ((e = strchr(t, '\n'))) - *e = 0; - - if (pa_atou(t, &pid) < 0) { - pa_log(__FILE__": WARNING: failed to parse PID file '%s'\n", fn); - return (pid_t) -1; - } - - return (pid_t) pid; -} - -static int open_pid_file(const char *fn, int mode) { - int fd = -1; - int lock = -1; - - for (;;) { - struct stat st; - - pa_make_secure_parent_dir(fn); - - if ((fd = open(fn, mode, S_IRUSR|S_IWUSR)) < 0) { - if (mode != O_RDONLY || errno != ENOENT) - pa_log(__FILE__": WARNING: failed to open PID file '%s': %s\n", fn, strerror(errno)); - goto fail; - } - - /* Try to lock the file. If that fails, go without */ - if (pa_lock_fd(fd, 1) < 0) - goto fail; - - if (fstat(fd, &st) < 0) { - pa_log(__FILE__": Failed to fstat() PID file '%s': %s\n", fn, strerror(errno)); - goto fail; - } - - /* Does the file still exist in the file system? When ye, w're done, otherwise restart */ - if (st.st_nlink >= 1) - break; - - if (pa_lock_fd(fd, 0) < 0) - goto fail; - - if (close(fd) < 0) { - pa_log(__FILE__": Failed to close file '%s': %s\n", fn, strerror(errno)); - goto fail; - } - - fd = -1; - } - - return fd; - -fail: - - if (fd < 0) { - if (lock >= 0) - pa_lock_fd(fd, 0); - - close(fd); - } - - return -1; -} - -/* Create a new PID file for the current process. */ -int pa_pid_file_create(void) { - int fd = -1; - int ret = -1; - char fn[PATH_MAX]; - char t[20]; - pid_t pid; - size_t l; - -#ifdef OS_IS_WIN32 - HANDLE process; -#endif - - pa_runtime_path("pid", fn, sizeof(fn)); - - if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0) - goto fail; - - if ((pid = read_pid(fn, fd)) == (pid_t) -1) - pa_log(__FILE__": corrupt PID file, overwriting.\n"); - else if (pid > 0) { -#ifdef OS_IS_WIN32 - if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) { - CloseHandle(process); -#else - if (kill(pid, 0) >= 0 || errno != ESRCH) { -#endif - pa_log(__FILE__": daemon already running.\n"); - goto fail; - } - - pa_log(__FILE__": stale PID file, overwriting.\n"); - } - - /* Overwrite the current PID file */ - if (lseek(fd, 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, 0) < 0) { - pa_log(__FILE__": failed to truncate PID fil: %s.\n", strerror(errno)); - goto fail; - } - - snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid()); - l = strlen(t); - - if (pa_loop_write(fd, t, l) != (ssize_t) l) { - pa_log(__FILE__": failed to write PID file.\n"); - goto fail; - } - - ret = 0; - -fail: - if (fd >= 0) { - pa_lock_fd(fd, 0); - close(fd); - } - - return ret; -} - -/* Remove the PID file, if it is ours */ -int pa_pid_file_remove(void) { - int fd = -1; - char fn[PATH_MAX]; - int ret = -1; - pid_t pid; - - pa_runtime_path("pid", fn, sizeof(fn)); - - if ((fd = open_pid_file(fn, O_RDWR)) < 0) { - pa_log(__FILE__": WARNING: failed to open PID file '%s': %s\n", fn, strerror(errno)); - goto fail; - } - - if ((pid = read_pid(fn, fd)) == (pid_t) -1) - goto fail; - - if (pid != getpid()) { - pa_log(__FILE__": WARNING: PID file '%s' not mine!\n", fn); - goto fail; - } - - if (ftruncate(fd, 0) < 0) { - pa_log(__FILE__": failed to truncate PID file '%s': %s\n", fn, strerror(errno)); - goto fail; - } - -#ifdef OS_IS_WIN32 - pa_lock_fd(fd, 0); - close(fd); - fd = -1; -#endif - - if (unlink(fn) < 0) { - pa_log(__FILE__": failed to remove PID file '%s': %s\n", fn, strerror(errno)); - goto fail; - } - - ret = 0; - -fail: - - if (fd >= 0) { - pa_lock_fd(fd, 0); - close(fd); - } - - return ret; -} - -/* Check whether the daemon is currently running, i.e. if a PID file - * exists and the PID therein too. Returns 0 on succcess, -1 - * otherwise. If pid is non-NULL and a running daemon was found, - * return its PID therein */ -int pa_pid_file_check_running(pid_t *pid) { - return pa_pid_file_kill(0, pid); -} - -#ifndef OS_IS_WIN32 - -/* Kill a current running daemon. Return non-zero on success, -1 - * otherwise. If successful *pid contains the PID of the daemon - * process. */ -int pa_pid_file_kill(int sig, pid_t *pid) { - int fd = -1; - char fn[PATH_MAX]; - int ret = -1; - pid_t _pid; - - if (!pid) - pid = &_pid; - - pa_runtime_path("pid", fn, sizeof(fn)); - - if ((fd = open_pid_file(fn, O_RDONLY)) < 0) - goto fail; - - if ((*pid = read_pid(fn, fd)) == (pid_t) -1) - goto fail; - - ret = kill(*pid, sig); - -fail: - - if (fd >= 0) { - pa_lock_fd(fd, 0); - close(fd); - } - - return ret; - -} - -#else /* OS_IS_WIN32 */ - -int pa_pid_file_kill(int sig, pid_t *pid) { - return -1; -} - -#endif diff --git a/polyp/pid.h b/polyp/pid.h deleted file mode 100644 index 906ab6da..00000000 --- a/polyp/pid.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef foopidhfoo -#define foopidhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -int pa_pid_file_create(void); -int pa_pid_file_remove(void); -int pa_pid_file_check_running(pid_t *pid); -int pa_pid_file_kill(int sig, pid_t *pid); - -#endif diff --git a/polyp/play-memchunk.c b/polyp/play-memchunk.c deleted file mode 100644 index e24d4427..00000000 --- a/polyp/play-memchunk.c +++ /dev/null @@ -1,118 +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 -#endif - -#include -#include -#include -#include - -#include "play-memchunk.h" -#include "sink-input.h" -#include "xmalloc.h" -#include "gccmacro.h" - -static void sink_input_kill(pa_sink_input *i) { - pa_memchunk *c; - assert(i && i->userdata); - c = i->userdata; - - pa_sink_input_disconnect(i); - pa_sink_input_unref(i); - - pa_memblock_unref(c->memblock); - pa_xfree(c); -} - -static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) { - pa_memchunk *c; - assert(i && chunk && i->userdata); - c = i->userdata; - - if (c->length <= 0) - return -1; - - assert(c->memblock && c->memblock->length); - *chunk = *c; - pa_memblock_ref(c->memblock); - - return 0; -} - -static void si_kill(PA_GCC_UNUSED pa_mainloop_api *m, void *i) { - sink_input_kill(i); -} - -static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) { - pa_memchunk *c; - assert(i && length && i->userdata); - c = i->userdata; - - assert(!memcmp(chunk, c, sizeof(chunk))); - assert(length <= c->length); - - c->length -= length; - c->index += length; - - if (c->length <= 0) - pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i); -} - -int pa_play_memchunk( - pa_sink *sink, - const char *name, - const pa_sample_spec *ss, - const pa_channel_map *map, - const pa_memchunk *chunk, - pa_cvolume *cvolume) { - - pa_sink_input *si; - pa_memchunk *nchunk; - - assert(sink); - assert(ss); - assert(chunk); - - if (cvolume && pa_cvolume_is_muted(cvolume)) - return 0; - - if (!(si = pa_sink_input_new(sink, name, __FILE__, ss, map, 0, PA_RESAMPLER_INVALID))) - return -1; - - if (cvolume) - si->volume = *cvolume; - - si->peek = sink_input_peek; - si->drop = sink_input_drop; - si->kill = sink_input_kill; - - si->userdata = nchunk = pa_xnew(pa_memchunk, 1); - *nchunk = *chunk; - - pa_memblock_ref(chunk->memblock); - - pa_sink_notify(sink); - - return 0; -} diff --git a/polyp/play-memchunk.h b/polyp/play-memchunk.h deleted file mode 100644 index fc0654a6..00000000 --- a/polyp/play-memchunk.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef fooplaychunkhfoo -#define fooplaychunkhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "sink.h" -#include "memchunk.h" - -int pa_play_memchunk( - pa_sink *sink, - const char *name, - const pa_sample_spec *ss, - const pa_channel_map *map, - const pa_memchunk *chunk, - pa_cvolume *cvolume); - -#endif diff --git a/polyp/poll.c b/polyp/poll.c deleted file mode 100644 index 193fc812..00000000 --- a/polyp/poll.c +++ /dev/null @@ -1,190 +0,0 @@ -/* $Id$ */ - -/*** - Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc. - Copyright (C) 2005, Cendio AB. - This file is part of polypaudio. - Based on work for the GNU C Library. - - polypaudio is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with polypaudio; If not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - USA. -***/ - -/* Poll the file descriptors described by the NFDS structures starting at - FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for - an event to occur; if TIMEOUT is -1, block until an event occurs. - Returns the number of file descriptors with events, zero if timed out, - or -1 for errors. */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#ifdef HAVE_SYS_SELECT_H -#include -#endif - -#include "winsock.h" - -#ifndef HAVE_SYS_POLL_H - -#include "util.h" -#include "poll.h" - -int poll (struct pollfd *fds, unsigned long int nfds, int timeout) { - struct timeval tv; - fd_set rset, wset, xset; - struct pollfd *f; - int ready; - int maxfd = 0; - char data[64]; - - FD_ZERO (&rset); - FD_ZERO (&wset); - FD_ZERO (&xset); - - if (nfds == 0) { - if (timeout >= 0) { - pa_msleep(timeout); - return 0; - } - -#ifdef OS_IS_WIN32 - /* - * Windows does not support signals properly so waiting for them would - * mean a deadlock. - */ - pa_msleep(100); - return 0; -#else - return select(0, NULL, NULL, NULL, NULL); -#endif - } - - for (f = fds; f < &fds[nfds]; ++f) { - if (f->fd != -1) { - if (f->events & POLLIN) - FD_SET (f->fd, &rset); - if (f->events & POLLOUT) - FD_SET (f->fd, &wset); - if (f->events & POLLPRI) - FD_SET (f->fd, &xset); - if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI))) - maxfd = f->fd; - } - } - - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - - ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset, - SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset, - SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv)); - if ((ready == -1) && (errno == EBADF)) { - ready = 0; - - FD_ZERO (&rset); - FD_ZERO (&wset); - FD_ZERO (&xset); - - maxfd = -1; - - for (f = fds; f < &fds[nfds]; ++f) { - if (f->fd != -1) { - fd_set sngl_rset, sngl_wset, sngl_xset; - - FD_ZERO (&sngl_rset); - FD_ZERO (&sngl_wset); - FD_ZERO (&sngl_xset); - - if (f->events & POLLIN) - FD_SET (f->fd, &sngl_rset); - if (f->events & POLLOUT) - FD_SET (f->fd, &sngl_wset); - if (f->events & POLLPRI) - FD_SET (f->fd, &sngl_xset); - if (f->events & (POLLIN|POLLOUT|POLLPRI)) { - struct timeval singl_tv; - - singl_tv.tv_sec = 0; - singl_tv.tv_usec = 0; - - if (select((SELECT_TYPE_ARG1) f->fd, SELECT_TYPE_ARG234 &rset, - SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset, - SELECT_TYPE_ARG5 &singl_tv) != -1) { - if (f->events & POLLIN) - FD_SET (f->fd, &rset); - if (f->events & POLLOUT) - FD_SET (f->fd, &wset); - if (f->events & POLLPRI) - FD_SET (f->fd, &xset); - if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI))) - maxfd = f->fd; - ++ready; - } else if (errno == EBADF) - f->revents |= POLLNVAL; - } - } - } - - if (ready) { - /* Linux alters the tv struct... but it shouldn't matter here ... - * as we're going to be a little bit out anyway as we've just eaten - * more than a couple of cpu cycles above */ - ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset, - SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset, - SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv)); - } - } - -#ifdef OS_IS_WIN32 - errno = WSAGetLastError(); -#endif - - if (ready > 0) { - ready = 0; - for (f = fds; f < &fds[nfds]; ++f) { - f->revents = 0; - if (f->fd != -1) { - if (FD_ISSET (f->fd, &rset)) { - /* support for POLLHUP. An hung up descriptor does not - increase the return value! */ - if (recv (f->fd, data, 64, MSG_PEEK) == -1) { - if (errno == ESHUTDOWN || errno == ECONNRESET || - errno == ECONNABORTED || errno == ENETRESET) { - fprintf(stderr, "Hangup\n"); - f->revents |= POLLHUP; - } - } - - if (f->revents == 0) - f->revents |= POLLIN; - } - if (FD_ISSET (f->fd, &wset)) - f->revents |= POLLOUT; - if (FD_ISSET (f->fd, &xset)) - f->revents |= POLLPRI; - } - if (f->revents) - ready++; - } - } - - return ready; -} - -#endif /* HAVE_SYS_POLL_H */ diff --git a/polyp/poll.h b/polyp/poll.h deleted file mode 100644 index c201014e..00000000 --- a/polyp/poll.h +++ /dev/null @@ -1,57 +0,0 @@ -/* $Id$ */ - -/*** - Compatibility definitions for System V `poll' interface. - Copyright (C) 1994,96,97,98,99,2000,2001,2004 Free Software Foundation, Inc. - Copyright (C) 2005, Cendio AB. - This file is part of polypaudio. - Based on work for the GNU C Library. - - polypaudio is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with polypaudio; If not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - USA. -***/ - -/* Event types that can be polled for. These bits may be set in `events' - to indicate the interesting event types; they will appear in `revents' - to indicate the status of the file descriptor. */ -#define POLLIN 0x001 /* There is data to read. */ -#define POLLPRI 0x002 /* There is urgent data to read. */ -#define POLLOUT 0x004 /* Writing now will not block. */ - -/* Event types always implicitly polled for. These bits need not be set in - `events', but they will appear in `revents' to indicate the status of - the file descriptor. */ -#define POLLERR 0x008 /* Error condition. */ -#define POLLHUP 0x010 /* Hung up. */ -#define POLLNVAL 0x020 /* Invalid polling request. */ - - -/* Type used for the number of file descriptors. */ -typedef unsigned long int nfds_t; - -/* Data structure describing a polling request. */ -struct pollfd - { - int fd; /* File descriptor to poll. */ - short int events; /* Types of events poller cares about. */ - short int revents; /* Types of events that actually occurred. */ - }; - -/* Poll the file descriptors described by the NFDS structures starting at - FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for - an event to occur; if TIMEOUT is -1, block until an event occurs. - Returns the number of file descriptors with events, zero if timed out, - or -1 for errors. */ -extern int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout); diff --git a/polyp/polyplib-browser.c b/polyp/polyplib-browser.c deleted file mode 100644 index 6fee84a4..00000000 --- a/polyp/polyplib-browser.c +++ /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 -#include - -#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." - -pa_browser { - int ref; - pa_mainloop_api *mainloop; - - void (*callback)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void *userdata); - void *callback_userdata; - - sw_discovery discovery; - pa_io_event *io_event; -}; - - -static void io_callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags events, void *userdata) { - pa_browser *b = userdata; - assert(a && b && b->mainloop == a); - - if (events != PA_IO_EVENT_INPUT || sw_discovery_read_socket(b->discovery) != SW_OKAY) { - pa_log(__FILE__": connection to HOWL daemon failed.\n"); - b->mainloop->io_free(b->io_event); - b->io_event = NULL; - return; - } -} - -static sw_result resolve_reply( - sw_discovery discovery, - sw_discovery_oid oid, - sw_uint32 interface_index, - sw_const_string name, - sw_const_string type, - sw_const_string domain, - sw_ipv4_address address, - sw_port port, - sw_octets text_record, - sw_ulong text_record_len, - sw_opaque extra) { - - pa_browser *b = extra; - pa_browse_info i; - char ip[256], a[256]; - pa_browse_opcode opcode; - int device_found = 0; - uint32_t cookie; - pa_typeid_t typeid; - pa_sample_spec ss; - int ss_valid = 0; - sw_text_record_iterator iterator; - int free_iterator = 0; - char *c = NULL; - - assert(b); - - sw_discovery_cancel(discovery, oid); - - memset(&i, 0, sizeof(i)); - i.name = name; - - if (!b->callback) - goto fail; - - if (!strcmp(type, SERVICE_NAME_SINK)) - opcode = PA_BROWSE_NEW_SINK; - else if (!strcmp(type, SERVICE_NAME_SOURCE)) - opcode = PA_BROWSE_NEW_SOURCE; - else if (!strcmp(type, SERVICE_NAME_SERVER)) - opcode = PA_BROWSE_NEW_SERVER; - else - goto fail; - - - snprintf(a, sizeof(a), "tcp:%s:%u", sw_ipv4_address_name(address, ip, sizeof(ip)), port); - i.server = a; - - if (text_record && text_record_len) { - char key[SW_TEXT_RECORD_MAX_LEN]; - uint8_t val[SW_TEXT_RECORD_MAX_LEN]; - uint32_t val_len; - - if (sw_text_record_iterator_init(&iterator, text_record, text_record_len) != SW_OKAY) { - pa_log("sw_text_record_string_iterator_init() failed.\n"); - goto fail; - } - - free_iterator = 1; - - while (sw_text_record_iterator_next(iterator, key, val, &val_len) == SW_OKAY) { - c = pa_xstrndup((char*) val, val_len); - - if (!strcmp(key, "device")) { - device_found = 1; - pa_xfree((char*) i.device); - i.device = c; - c = NULL; - } else if (!strcmp(key, "server-version")) { - pa_xfree((char*) i.server_version); - i.server_version = c; - c = NULL; - } else if (!strcmp(key, "user-name")) { - pa_xfree((char*) i.user_name); - i.user_name = c; - c = NULL; - } else if (!strcmp(key, "fqdn")) { - size_t l; - - pa_xfree((char*) i.fqdn); - i.fqdn = c; - c = NULL; - - l = strlen(a); - assert(l+1 <= sizeof(a)); - strncat(a, " ", sizeof(a)-l-1); - strncat(a, i.fqdn, sizeof(a)-l-2); - } else if (!strcmp(key, "cookie")) { - - if (pa_atou(c, &cookie) < 0) - goto fail; - - i.cookie = &cookie; - } else if (!strcmp(key, "description")) { - pa_xfree((char*) i.description); - i.description = c; - c = NULL; - } else if (!strcmp(key, "typeid")) { - - if (pa_atou(c, &typeid) < 0) - goto fail; - - i.typeid = &typeid; - } else if (!strcmp(key, "channels")) { - uint32_t ch; - - if (pa_atou(c, &ch) < 0 || ch <= 0 || ch > 255) - goto fail; - - ss.channels = (uint8_t) ch; - ss_valid |= 1; - - } else if (!strcmp(key, "rate")) { - if (pa_atou(c, &ss.rate) < 0) - goto fail; - ss_valid |= 2; - } else if (!strcmp(key, "format")) { - - if ((ss.format = pa_parse_sample_format(c)) == PA_SAMPLE_INVALID) - goto fail; - - ss_valid |= 4; - } - - pa_xfree(c); - c = NULL; - } - - } - - /* No device txt record was sent for a sink or source service */ - if (opcode != PA_BROWSE_NEW_SERVER && !device_found) - goto fail; - - if (ss_valid == 7) - i.sample_spec = &ss; - - - b->callback(b, opcode, &i, b->callback_userdata); - -fail: - pa_xfree((void*) i.device); - pa_xfree((void*) i.fqdn); - pa_xfree((void*) i.server_version); - pa_xfree((void*) i.user_name); - pa_xfree((void*) i.description); - pa_xfree(c); - - if (free_iterator) - sw_text_record_iterator_fina(iterator); - - - return SW_OKAY; -} - -static sw_result browse_reply( - sw_discovery discovery, - sw_discovery_oid id, - sw_discovery_browse_status status, - sw_uint32 interface_index, - sw_const_string name, - sw_const_string type, - sw_const_string domain, - sw_opaque extra) { - - pa_browser *b = extra; - assert(b); - - switch (status) { - case SW_DISCOVERY_BROWSE_ADD_SERVICE: { - sw_discovery_oid oid; - - if (sw_discovery_resolve(b->discovery, 0, name, type, domain, resolve_reply, b, &oid) != SW_OKAY) - pa_log("sw_discovery_resolve() failed\n"); - - break; - } - - case SW_DISCOVERY_BROWSE_REMOVE_SERVICE: - if (b->callback) { - pa_browse_info i; - memset(&i, 0, sizeof(i)); - i.name = name; - b->callback(b, PA_BROWSE_REMOVE, &i, b->callback_userdata); - } - break; - - default: - ; - } - - return SW_OKAY; -} - -pa_browser *pa_browser_new(pa_mainloop_api *mainloop) { - pa_browser *b; - sw_discovery_oid oid; - - b = pa_xmalloc(sizeof(pa_browser)); - b->mainloop = mainloop; - b->ref = 1; - b->callback = NULL; - b->callback_userdata = NULL; - - if (sw_discovery_init(&b->discovery) != SW_OKAY) { - pa_log("sw_discovery_init() failed.\n"); - pa_xfree(b); - return NULL; - } - - if (sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SERVER, NULL, browse_reply, b, &oid) != SW_OKAY || - sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SINK, NULL, browse_reply, b, &oid) != SW_OKAY || - sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SOURCE, NULL, browse_reply, b, &oid) != SW_OKAY) { - - pa_log("sw_discovery_browse() failed.\n"); - - sw_discovery_fina(b->discovery); - pa_xfree(b); - return NULL; - } - - b->io_event = mainloop->io_new(mainloop, sw_discovery_socket(b->discovery), PA_IO_EVENT_INPUT, io_callback, b); - return b; -} - -static void browser_free(pa_browser *b) { - assert(b && b->mainloop); - - if (b->io_event) - b->mainloop->io_free(b->io_event); - - sw_discovery_fina(b->discovery); - pa_xfree(b); -} - -pa_browser *pa_browser_ref(pa_browser *b) { - assert(b && b->ref >= 1); - b->ref++; - return b; -} - -void pa_browser_unref(pa_browser *b) { - assert(b && b->ref >= 1); - - if ((-- (b->ref)) <= 0) - browser_free(b); -} - -void pa_browser_set_callback(pa_browser *b, void (*cb)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void* userdata), void *userdata) { - assert(b); - - b->callback = cb; - b->callback_userdata = userdata; -} diff --git a/polyp/polyplib-browser.h b/polyp/polyplib-browser.h deleted file mode 100644 index 853304d7..00000000 --- a/polyp/polyplib-browser.h +++ /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 -#include -#include -#include - -PA_C_DECL_BEGIN - -pa_browser; - -pa_browse_opcode { - PA_BROWSE_NEW_SERVER, - PA_BROWSE_NEW_SINK, - PA_BROWSE_NEW_SOURCE, - PA_BROWSE_REMOVE -}; - -pa_browser *pa_browser_new(pa_mainloop_api *mainloop); -pa_browser *pa_browser_ref(pa_browser *z); -void pa_browser_unref(pa_browser *z); - -pa_browse_info { - /* Unique service name */ - const char *name; /* always available */ - - /* Server info */ - const char *server; /* always available */ - const char *server_version, *user_name, *fqdn; /* optional */ - const uint32_t *cookie; /* optional */ - - /* Device info */ - const char *device; /* always available when this information is of a sink/source */ - const char *description; /* optional */ - const pa_typeid_t *typeid; /* optional */ - const pa_sample_spec *sample_spec; /* optional */ -}; - -void pa_browser_set_callback(pa_browser *z, void (*cb)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void *userdata), void *userdata); - -PA_C_DECL_END - -#endif diff --git a/polyp/polyplib-context.c b/polyp/polyplib-context.c deleted file mode 100644 index 15f2e4cd..00000000 --- a/polyp/polyplib-context.c +++ /dev/null @@ -1,871 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SYS_WAIT_H -#include -#endif - -#ifdef HAVE_SYS_SOCKET_H -#include -#endif -#ifdef HAVE_NETDB_H -#include -#endif - -#include "winsock.h" - -#include "polyplib-internal.h" -#include "polyplib-context.h" -#include "polyplib-version.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 pa_pdispatch_callback command_table[PA_COMMAND_MAX] = { - [PA_COMMAND_REQUEST] = pa_command_request, - [PA_COMMAND_PLAYBACK_STREAM_KILLED] = pa_command_stream_killed, - [PA_COMMAND_RECORD_STREAM_KILLED] = pa_command_stream_killed, - [PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event -}; - -static void unlock_autospawn_lock_file(pa_context *c) { - assert(c); - - if (c->autospawn_lock_fd >= 0) { - char lf[PATH_MAX]; - pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf)); - - pa_unlock_lockfile(lf, c->autospawn_lock_fd); - c->autospawn_lock_fd = -1; - } -} - -pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { - pa_context *c; - assert(mainloop && name); - - c = pa_xmalloc(sizeof(pa_context)); - c->ref = 1; - c->name = pa_xstrdup(name); - c->mainloop = mainloop; - c->client = NULL; - c->pstream = NULL; - c->pdispatch = NULL; - c->playback_streams = pa_dynarray_new(); - c->record_streams = pa_dynarray_new(); - assert(c->playback_streams && c->record_streams); - - PA_LLIST_HEAD_INIT(pa_stream, c->streams); - PA_LLIST_HEAD_INIT(pa_operation, c->operations); - - c->error = PA_ERROR_OK; - c->state = PA_CONTEXT_UNCONNECTED; - c->ctag = 0; - - c->state_callback = NULL; - c->state_userdata = NULL; - - c->subscribe_callback = NULL; - c->subscribe_userdata = NULL; - - c->memblock_stat = pa_memblock_stat_new(); - c->local = -1; - c->server_list = NULL; - c->server = NULL; - c->autospawn_lock_fd = -1; - memset(&c->spawn_api, 0, sizeof(c->spawn_api)); - c->do_autospawn = 0; - -#ifdef SIGPIPE - pa_check_signal_is_blocked(SIGPIPE); -#endif - - c->conf = pa_client_conf_new(); - pa_client_conf_load(c->conf, NULL); -#ifdef HAVE_X11 - pa_client_conf_from_x11(c->conf, NULL); -#endif - pa_client_conf_env(c->conf); - - return c; -} - -static void context_free(pa_context *c) { - assert(c); - - unlock_autospawn_lock_file(c); - - while (c->operations) - pa_operation_cancel(c->operations); - - while (c->streams) - pa_stream_set_state(c->streams, PA_STREAM_TERMINATED); - - if (c->client) - pa_socket_client_unref(c->client); - if (c->pdispatch) - pa_pdispatch_unref(c->pdispatch); - if (c->pstream) { - pa_pstream_close(c->pstream); - pa_pstream_unref(c->pstream); - } - - if (c->record_streams) - pa_dynarray_free(c->record_streams, NULL, NULL); - if (c->playback_streams) - pa_dynarray_free(c->playback_streams, NULL, NULL); - - pa_memblock_stat_unref(c->memblock_stat); - - if (c->conf) - pa_client_conf_free(c->conf); - - pa_strlist_free(c->server_list); - - pa_xfree(c->name); - pa_xfree(c->server); - pa_xfree(c); -} - -pa_context* pa_context_ref(pa_context *c) { - assert(c && c->ref >= 1); - c->ref++; - return c; -} - -void pa_context_unref(pa_context *c) { - assert(c && c->ref >= 1); - - if ((--(c->ref)) == 0) - context_free(c); -} - -void pa_context_set_state(pa_context *c, pa_context_state_t st) { - assert(c); - - if (c->state == st) - return; - - pa_context_ref(c); - - if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) { - pa_stream *s; - - s = c->streams ? pa_stream_ref(c->streams) : NULL; - while (s) { - pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL; - pa_stream_set_state(s, st == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED); - pa_stream_unref(s); - s = n; - } - - if (c->pdispatch) - pa_pdispatch_unref(c->pdispatch); - c->pdispatch = NULL; - - if (c->pstream) { - pa_pstream_close(c->pstream); - pa_pstream_unref(c->pstream); - } - c->pstream = NULL; - - if (c->client) - pa_socket_client_unref(c->client); - c->client = NULL; - } - - c->state = st; - if (c->state_callback) - c->state_callback(c, c->state_userdata); - - pa_context_unref(c); -} - -void pa_context_fail(pa_context *c, int error) { - assert(c); - c->error = error; - pa_context_set_state(c, PA_CONTEXT_FAILED); -} - -static void pstream_die_callback(pa_pstream *p, void *userdata) { - pa_context *c = userdata; - assert(p && c); - pa_context_fail(c, PA_ERROR_CONNECTIONTERMINATED); -} - -static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) { - pa_context *c = userdata; - assert(p && packet && c); - - pa_context_ref(c); - - if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) { - pa_log(__FILE__": invalid packet.\n"); - pa_context_fail(c, PA_ERROR_PROTOCOL); - } - - pa_context_unref(c); -} - -static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, PA_GCC_UNUSED uint32_t delta, const pa_memchunk *chunk, void *userdata) { - pa_context *c = userdata; - pa_stream *s; - assert(p && chunk && c && chunk->memblock && chunk->memblock->data); - - pa_context_ref(c); - - if ((s = pa_dynarray_get(c->record_streams, channel))) { - pa_mcalign_push(s->mcalign, chunk); - - for (;;) { - pa_memchunk t; - - if (pa_mcalign_pop(s->mcalign, &t) < 0) - break; - - if (s->read_callback) { - s->read_callback(s, (uint8_t*) t.memblock->data + t.index, t.length, s->read_userdata); - s->counter += chunk->length; - } - - pa_memblock_unref(t.memblock); - } - } - - pa_context_unref(c); -} - -int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) { - assert(c); - - if (command == PA_COMMAND_ERROR) { - assert(t); - - if (pa_tagstruct_getu32(t, &c->error) < 0) { - pa_context_fail(c, PA_ERROR_PROTOCOL); - return -1; - - } - } else if (command == PA_COMMAND_TIMEOUT) - c->error = PA_ERROR_TIMEOUT; - else { - pa_context_fail(c, PA_ERROR_PROTOCOL); - return -1; - } - - return 0; -} - -static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_context *c = userdata; - assert(pd && c && (c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME)); - - pa_context_ref(c); - - if (command != PA_COMMAND_REPLY) { - - if (pa_context_handle_error(c, command, t) < 0) - pa_context_fail(c, PA_ERROR_PROTOCOL); - - pa_context_fail(c, c->error); - goto finish; - } - - switch(c->state) { - case PA_CONTEXT_AUTHORIZING: { - pa_tagstruct *reply; - reply = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME); - pa_tagstruct_putu32(reply, tag = c->ctag++); - pa_tagstruct_puts(reply, c->name); - pa_pstream_send_tagstruct(c->pstream, reply); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c); - - pa_context_set_state(c, PA_CONTEXT_SETTING_NAME); - break; - } - - case PA_CONTEXT_SETTING_NAME : - pa_context_set_state(c, PA_CONTEXT_READY); - break; - - default: - assert(0); - } - -finish: - pa_context_unref(c); -} - -static void setup_context(pa_context *c, pa_iochannel *io) { - pa_tagstruct *t; - uint32_t tag; - assert(c && io); - - pa_context_ref(c); - - assert(!c->pstream); - c->pstream = pa_pstream_new(c->mainloop, io, c->memblock_stat); - assert(c->pstream); - - pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c); - pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c); - pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c); - - assert(!c->pdispatch); - c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX); - assert(c->pdispatch); - - if (!c->conf->cookie_valid) { - pa_context_fail(c, PA_ERROR_AUTHKEY); - goto finish; - } - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_AUTH); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie)); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c); - - pa_context_set_state(c, PA_CONTEXT_AUTHORIZING); - -finish: - - pa_context_unref(c); -} - -static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata); - -#ifndef OS_IS_WIN32 - -static int context_connect_spawn(pa_context *c) { - pid_t pid; - int status, r; - int fds[2] = { -1, -1} ; - pa_iochannel *io; - - pa_context_ref(c); - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { - pa_log(__FILE__": socketpair() failed: %s\n", strerror(errno)); - pa_context_fail(c, PA_ERROR_INTERNAL); - goto fail; - } - - pa_fd_set_cloexec(fds[0], 1); - - pa_socket_low_delay(fds[0]); - pa_socket_low_delay(fds[1]); - - if (c->spawn_api.prefork) - c->spawn_api.prefork(); - - if ((pid = fork()) < 0) { - pa_log(__FILE__": fork() failed: %s\n", strerror(errno)); - pa_context_fail(c, PA_ERROR_INTERNAL); - - if (c->spawn_api.postfork) - c->spawn_api.postfork(); - - goto fail; - } else if (!pid) { - /* Child */ - - char t[128]; - const char *state = NULL; -#define MAX_ARGS 64 - const char * argv[MAX_ARGS+1]; - int n; - - /* Not required, since fds[0] has CLOEXEC enabled anyway */ - close(fds[0]); - - if (c->spawn_api.atfork) - c->spawn_api.atfork(); - - /* Setup argv */ - - n = 0; - - argv[n++] = c->conf->daemon_binary; - argv[n++] = "--daemonize=yes"; - - snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]); - argv[n++] = strdup(t); - - while (n < MAX_ARGS) { - char *a; - - if (!(a = pa_split_spaces(c->conf->extra_arguments, &state))) - break; - - argv[n++] = a; - } - - argv[n++] = NULL; - - execv(argv[0], (char * const *) argv); - _exit(1); -#undef MAX_ARGS - } - - /* Parent */ - - r = waitpid(pid, &status, 0); - - if (c->spawn_api.postfork) - c->spawn_api.postfork(); - - if (r < 0) { - pa_log(__FILE__": waitpid() failed: %s\n", strerror(errno)); - pa_context_fail(c, PA_ERROR_INTERNAL); - goto fail; - } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED); - goto fail; - } - - close(fds[1]); - - c->local = 1; - - io = pa_iochannel_new(c->mainloop, fds[0], fds[0]); - - setup_context(c, io); - unlock_autospawn_lock_file(c); - - pa_context_unref(c); - - return 0; - -fail: - if (fds[0] != -1) - close(fds[0]); - if (fds[1] != -1) - close(fds[1]); - - unlock_autospawn_lock_file(c); - - pa_context_unref(c); - - return -1; -} - -#endif /* OS_IS_WIN32 */ - -static int try_next_connection(pa_context *c) { - char *u = NULL; - int r = -1; - assert(c && !c->client); - - for (;;) { - if (u) - pa_xfree(u); - u = NULL; - - c->server_list = pa_strlist_pop(c->server_list, &u); - - if (!u) { - -#ifndef OS_IS_WIN32 - if (c->do_autospawn) { - r = context_connect_spawn(c); - goto finish; - } -#endif - - pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED); - goto finish; - } - - pa_log_debug(__FILE__": Trying to connect to %s...\n", u); - - pa_xfree(c->server); - c->server = pa_xstrdup(u); - - if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT))) - continue; - - c->local = pa_socket_client_is_local(c->client); - pa_socket_client_set_callback(c->client, on_connection, c); - break; - } - - r = 0; - -finish: - if (u) - pa_xfree(u); - - return r; -} - -static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) { - pa_context *c = userdata; - assert(client && c && c->state == PA_CONTEXT_CONNECTING); - - pa_context_ref(c); - - pa_socket_client_unref(client); - c->client = NULL; - - if (!io) { - /* Try the item in the list */ - if (errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EHOSTUNREACH) { - try_next_connection(c); - goto finish; - } - - pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED); - goto finish; - } - - unlock_autospawn_lock_file(c); - setup_context(c, io); - -finish: - pa_context_unref(c); -} - -int pa_context_connect(pa_context *c, const char *server, int spawn, const pa_spawn_api *api) { - int r = -1; - assert(c && c->ref >= 1 && c->state == PA_CONTEXT_UNCONNECTED); - - if (!server) - server = c->conf->default_server; - - pa_context_ref(c); - - assert(!c->server_list); - - if (server) { - if (!(c->server_list = pa_strlist_parse(server))) { - pa_context_fail(c, PA_ERROR_INVALIDSERVER); - goto finish; - } - } else { - char *d; - char ufn[PATH_MAX]; - - /* Prepend in reverse order */ - - if ((d = getenv("DISPLAY"))) { - char *e; - d = pa_xstrdup(d); - if ((e = strchr(d, ':'))) - *e = 0; - - if (*d) - c->server_list = pa_strlist_prepend(c->server_list, d); - - pa_xfree(d); - } - - c->server_list = pa_strlist_prepend(c->server_list, "tcp6:localhost"); - c->server_list = pa_strlist_prepend(c->server_list, "localhost"); - c->server_list = pa_strlist_prepend(c->server_list, pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET, ufn, sizeof(ufn))); - - /* Wrap the connection attempts in a single transaction for sane autospawn locking */ - if (spawn && c->conf->autospawn) { - char lf[PATH_MAX]; - - pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf)); - pa_make_secure_parent_dir(lf); - assert(c->autospawn_lock_fd <= 0); - c->autospawn_lock_fd = pa_lock_lockfile(lf); - - if (api) - c->spawn_api = *api; - c->do_autospawn = 1; - } - - } - - pa_context_set_state(c, PA_CONTEXT_CONNECTING); - r = try_next_connection(c); - -finish: - pa_context_unref(c); - - return r; -} - -void pa_context_disconnect(pa_context *c) { - assert(c); - pa_context_set_state(c, PA_CONTEXT_TERMINATED); -} - -pa_context_state_t pa_context_get_state(pa_context *c) { - assert(c && c->ref >= 1); - return c->state; -} - -int pa_context_errno(pa_context *c) { - assert(c && c->ref >= 1); - return c->error; -} - -void pa_context_set_state_callback(pa_context *c, void (*cb)(pa_context *c, void *userdata), void *userdata) { - assert(c && c->ref >= 1); - c->state_callback = cb; - c->state_userdata = userdata; -} - -int pa_context_is_pending(pa_context *c) { - assert(c && c->ref >= 1); - -/* pa_log("pstream: %i\n", pa_pstream_is_pending(c->pstream)); */ -/* pa_log("pdispatch: %i\n", pa_pdispatch_is_pending(c->pdispatch)); */ - - return (c->pstream && pa_pstream_is_pending(c->pstream)) || - (c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) || - c->client; -} - -static void set_dispatch_callbacks(pa_operation *o); - -static void pdispatch_drain_callback(PA_GCC_UNUSED pa_pdispatch*pd, void *userdata) { - set_dispatch_callbacks(userdata); -} - -static void pstream_drain_callback(PA_GCC_UNUSED pa_pstream *s, void *userdata) { - set_dispatch_callbacks(userdata); -} - -static void set_dispatch_callbacks(pa_operation *o) { - int done = 1; - assert(o && o->context && o->context->ref >= 1 && o->ref >= 1 && o->context->state == PA_CONTEXT_READY); - - pa_pstream_set_drain_callback(o->context->pstream, NULL, NULL); - pa_pdispatch_set_drain_callback(o->context->pdispatch, NULL, NULL); - - if (pa_pdispatch_is_pending(o->context->pdispatch)) { - pa_pdispatch_set_drain_callback(o->context->pdispatch, pdispatch_drain_callback, o); - done = 0; - } - - if (pa_pstream_is_pending(o->context->pstream)) { - pa_pstream_set_drain_callback(o->context->pstream, pstream_drain_callback, o); - done = 0; - } - - if (!done) - pa_operation_ref(o); - else { - if (o->callback) { - void (*cb)(pa_context *c, void *userdata); - cb = (void (*)(pa_context*, void*)) o->callback; - cb(o->context, o->userdata); - } - - pa_operation_done(o); - } - - pa_operation_unref(o); -} - -pa_operation* pa_context_drain(pa_context *c, void (*cb) (pa_context*c, void *userdata), void *userdata) { - pa_operation *o; - assert(c && c->ref >= 1); - - if (c->state != PA_CONTEXT_READY) - return NULL; - - if (!pa_context_is_pending(c)) - return NULL; - - o = pa_operation_new(c, NULL); - assert(o); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - set_dispatch_callbacks(pa_operation_ref(o)); - - return o; -} - -void pa_context_exit_daemon(pa_context *c) { - pa_tagstruct *t; - assert(c && c->ref >= 1); - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_EXIT); - pa_tagstruct_putu32(t, c->ctag++); - pa_pstream_send_tagstruct(c->pstream, t); -} - -void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - int success = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - success = 0; - } else if (!pa_tagstruct_eof(t)) { - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *c, int _success, void *_userdata) = (void (*)(pa_context *c, int _success, void *_userdata)) o->callback; - cb(o->context, success, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, command); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_SET_DEFAULT_SINK); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_puts(t, name); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_set_default_source(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_SET_DEFAULT_SOURCE); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_puts(t, name); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); - - return pa_operation_ref(o); -} - -int pa_context_is_local(pa_context *c) { - assert(c); - return c->local; -} - -pa_operation* pa_context_set_name(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && name && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_SET_CLIENT_NAME); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_puts(t, name); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); - - return pa_operation_ref(o); -} - -const char* pa_get_library_version(void) { - return PACKAGE_VERSION; -} - -const char* pa_context_get_server(pa_context *c) { - - if (!c->server) - return NULL; - - if (*c->server == '{') { - char *e = strchr(c->server+1, '}'); - return e ? e+1 : c->server; - } - - return c->server; -} diff --git a/polyp/polyplib-context.h b/polyp/polyplib-context.h deleted file mode 100644 index febb75f4..00000000 --- a/polyp/polyplib-context.h +++ /dev/null @@ -1,117 +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 -#include -#include -#include -#include - -/** \file - * Connection contexts for asynchrononous communication with a - * server. A pa_context object wraps a connection to a polypaudio - * server using its native protocol. A context may be used to issue - * commands on the server or to create playback or recording - * streams. Multiple playback streams may be piped through a single - * connection context. Operations on the contect involving - * communication with the server are executed asynchronously: i.e. the - * client function do not implicitely wait for completion of the - * operation on the server. Instead the caller specifies a call back - * function that is called when the operation is completed. Currently - * running operations may be canceled using pa_operation_cancel(). */ - -/** \example pacat.c - * A playback and recording tool using the asynchronous API */ - -/** \example paplay.c - * A sound file playback tool using the asynchronous API, based on libsndfile */ - -PA_C_DECL_BEGIN - -/** \pa_context - * An opaque connection context to a daemon */ -typedef struct pa_context pa_context; - -/** Instantiate a new connection context with an abstract mainloop API - * and an application name */ -pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name); - -/** Decrease the reference counter of the context by one */ -void pa_context_unref(pa_context *c); - -/** Increase the reference counter of the context by one */ -pa_context* pa_context_ref(pa_context *c); - -typedef void (*pa_context_state_callback)(pa_context *c, void *userdata); - -/** Set a callback function that is called whenever the context status changes */ -void pa_context_set_state_callback(pa_context *c, pa_context_state_callback callback, void *userdata); - -/** Return the error number of the last failed operation */ -int pa_context_errno(pa_context *c); - -/** Return non-zero if some data is pending to be written to the connection */ -int pa_context_is_pending(pa_context *c); - -/** Return the current context status */ -pa_context_state_t pa_context_get_state(pa_context *c); - -/** Connect the context to the specified server. If server is NULL, -connect to the default server. This routine may but will not always -return synchronously on error. Use pa_context_set_state_callback() to -be notified when the connection is established. If spawn is non-zero -and no specific server is specified or accessible a new daemon is -spawned. If api is non-NULL, the functions specified in the structure -are used when forking a new child process. */ -int pa_context_connect(pa_context *c, const char *server, int spawn, const pa_spawn_api *api); - -/** Terminate the context connection immediately */ -void pa_context_disconnect(pa_context *c); - -/** Drain the context. If there is nothing to drain, the function returns NULL */ -pa_operation* pa_context_drain(pa_context *c, void (*cb) (pa_context*c, void *userdata), void *userdata); - -/** Tell the daemon to exit. No operation object is returned as the - * connection is terminated when the daemon quits, thus this operation - * would never complete. */ -void pa_context_exit_daemon(pa_context *c); - -/** Set the name of the default sink. \since 0.4 */ -pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata); - -/** Set the name of the default source. \since 0.4 */ -pa_operation* pa_context_set_default_source(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata); - -/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. \since 0.5 */ -int pa_context_is_local(pa_context *c); - -/** Set a different application name for context on the server. \since 0.5 */ -pa_operation* pa_context_set_name(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata); - -/** Return the server name this context is connected to. \since 0.7 */ -const char* pa_context_get_server(pa_context *c); - -PA_C_DECL_END - -#endif diff --git a/polyp/polyplib-def.h b/polyp/polyplib-def.h deleted file mode 100644 index 0591ce6c..00000000 --- a/polyp/polyplib-def.h +++ /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 -#include -#include - -#include -#include - -/** \file - * Global definitions */ - -PA_C_DECL_BEGIN - -/** The state of a connection context */ -typedef enum pa_context_state { - PA_CONTEXT_UNCONNECTED, /**< The context hasn't been connected yet */ - PA_CONTEXT_CONNECTING, /**< A connection is being established */ - PA_CONTEXT_AUTHORIZING, /**< The client is authorizing itself to the daemon */ - PA_CONTEXT_SETTING_NAME, /**< The client is passing its application name to the daemon */ - PA_CONTEXT_READY, /**< The connection is established, the context is ready to execute operations */ - PA_CONTEXT_FAILED, /**< The connection failed or was disconnected */ - PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */ -} pa_context_state_t; - -/** The state of a stream */ -typedef enum pa_stream_state { - PA_STREAM_DISCONNECTED, /**< The stream is not yet connected to any sink or source */ - PA_STREAM_CREATING, /**< The stream is being created */ - PA_STREAM_READY, /**< The stream is established, you may pass audio data to it now */ - PA_STREAM_FAILED, /**< An error occured that made the stream invalid */ - PA_STREAM_TERMINATED /**< The stream has been terminated cleanly */ -} pa_stream_state_t; - -/** The state of an operation */ -typedef enum pa_operation_state { - PA_OPERATION_RUNNING, /**< The operation is still running */ - PA_OPERATION_DONE, /**< The operation has been completed */ - PA_OPERATION_CANCELED /**< The operation has been canceled */ -} pa_operation_state_t; - -/** An invalid index */ -#define PA_INVALID_INDEX ((uint32_t) -1) - -/** The direction of a pa_stream object */ -typedef enum pa_stream_direction { - PA_STREAM_NODIRECTION, /**< Invalid direction */ - PA_STREAM_PLAYBACK, /**< Playback stream */ - PA_STREAM_RECORD, /**< Record stream */ - PA_STREAM_UPLOAD /**< Sample upload stream */ -} pa_stream_direction_t; - -/** Some special flags for stream connections. \since 0.6 */ -typedef enum pa_stream_flags { - PA_STREAM_START_CORKED = 1, /**< Create the stream corked, requiring an explicit pa_stream_cork() call to uncork it. */ - PA_STREAM_INTERPOLATE_LATENCY = 2 /**< Interpolate the latency for - * this stream. When enabled, - * you can use - * pa_stream_interpolated_xxx() - * for synchronization. Using - * these functions instead of - * pa_stream_get_latency() has - * the advantage of not - * requiring a whole roundtrip - * for responses. Consider using - * this option when frequently - * requesting latency - * information. This is - * especially useful on long latency - * network connections. */ -} pa_stream_flags_t; - -/** Playback and record buffer metrics */ -typedef struct pa_buffer_attr { - uint32_t maxlength; /**< Maximum length of the buffer */ - uint32_t tlength; /**< Playback only: target length of the buffer. The server tries to assure that at least tlength bytes are always available in the buffer */ - uint32_t prebuf; /**< Playback only: pre-buffering. The server does not start with playback before at least prebug bytes are available in the buffer */ - uint32_t minreq; /**< Playback only: minimum request. The server does not request less than minreq bytes from the client, instead waints until the buffer is free enough to request more bytes at once */ - uint32_t fragsize; /**< Recording only: fragment size. The server sends data in blocks of fragsize bytes size. Large values deminish interactivity with other operations on the connection context but decrease control overhead. */ -} pa_buffer_attr; - -/** Error values as used by pa_context_errno(). Use pa_strerror() to convert these values to human readable strings */ -enum { - PA_ERROR_OK, /**< No error */ - PA_ERROR_ACCESS, /**< Access failure */ - PA_ERROR_COMMAND, /**< Unknown command */ - PA_ERROR_INVALID, /**< Invalid argument */ - PA_ERROR_EXIST, /**< Entity exists */ - PA_ERROR_NOENTITY, /**< No such entity */ - PA_ERROR_CONNECTIONREFUSED, /**< Connection refused */ - PA_ERROR_PROTOCOL, /**< Protocol error */ - PA_ERROR_TIMEOUT, /**< Timeout */ - PA_ERROR_AUTHKEY, /**< No authorization key */ - PA_ERROR_INTERNAL, /**< Internal error */ - PA_ERROR_CONNECTIONTERMINATED, /**< Connection terminated */ - PA_ERROR_KILLED, /**< Entity killed */ - PA_ERROR_INVALIDSERVER, /**< Invalid server */ - PA_ERROR_INITFAILED, /**< Module initialization failed */ - PA_ERROR_MAX /**< Not really an error but the first invalid error code */ -}; - -/** Subscription event mask, as used by pa_context_subscribe() */ -typedef enum pa_subscription_mask { - PA_SUBSCRIPTION_MASK_NULL = 0, /**< No events */ - PA_SUBSCRIPTION_MASK_SINK = 1, /**< Sink events */ - PA_SUBSCRIPTION_MASK_SOURCE = 2, /**< Source events */ - PA_SUBSCRIPTION_MASK_SINK_INPUT = 4, /**< Sink input events */ - PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT = 8, /**< Source output events */ - PA_SUBSCRIPTION_MASK_MODULE = 16, /**< Module events */ - PA_SUBSCRIPTION_MASK_CLIENT = 32, /**< Client events */ - PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 64, /**< Sample cache events */ - PA_SUBSCRIPTION_MASK_SERVER = 128, /**< Other global server changes. \since 0.4 */ - PA_SUBSCRIPTION_MASK_AUTOLOAD = 256 /**< Autoload table events. \since 0.5 */ -} pa_subscription_mask_t; - -/** Subscription event types, as used by pa_context_subscribe() */ -typedef enum pa_subscription_event_type { - PA_SUBSCRIPTION_EVENT_SINK = 0, /**< Event type: Sink */ - PA_SUBSCRIPTION_EVENT_SOURCE = 1, /**< Event type: Source */ - PA_SUBSCRIPTION_EVENT_SINK_INPUT = 2, /**< Event type: Sink input */ - PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT = 3, /**< Event type: Source output */ - PA_SUBSCRIPTION_EVENT_MODULE = 4, /**< Event type: Module */ - PA_SUBSCRIPTION_EVENT_CLIENT = 5, /**< Event type: Client */ - PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 6, /**< Event type: Sample cache item */ - PA_SUBSCRIPTION_EVENT_SERVER = 7, /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. \since 0.4 */ - PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8, /**< Event type: Autoload table changes. \since 0.5 */ - PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 15, /**< A mask to extract the event type from an event value */ - - PA_SUBSCRIPTION_EVENT_NEW = 0, /**< A new object was created */ - PA_SUBSCRIPTION_EVENT_CHANGE = 16, /**< A property of the object was modified */ - PA_SUBSCRIPTION_EVENT_REMOVE = 32, /**< An object was removed */ - PA_SUBSCRIPTION_EVENT_TYPE_MASK = 16+32 /**< A mask to extract the event operation from an event value */ -} pa_subscription_event_type_t; - -/** Return one if an event type t matches an event mask bitfield */ -#define pa_subscription_match_flags(m, t) (!!((m) & (1 << ((t) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)))) - -/** A structure for latency info. See pa_stream_get_latency(). The - * total output latency a sample that is written with - * pa_stream_write() takes to be played may be estimated by - * sink_usec+buffer_usec+transport_usec. The output buffer to which - * buffer_usec relates may be manipulated freely (with - * pa_stream_write()'s delta argument, pa_stream_flush() and friends), - * the buffers sink_usec/source_usec relates to is a first-in - * first-out buffer which cannot be flushed or manipulated in any - * way. The total input latency a sample that is recorded takes to be - * delivered to the application is: - * source_usec+buffer_usec+transport_usec-sink_usec. (Take care of - * sign issues!) When connected to a monitor source sink_usec contains - * the latency of the owning sink.*/ -typedef struct pa_latency_info { - pa_usec_t buffer_usec; /**< Time in usecs the current buffer takes to play. For both playback and record streams. */ - pa_usec_t sink_usec; /**< Time in usecs a sample takes to be played on the sink. For playback streams and record streams connected to a monitor source. */ - pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. \since 0.5*/ - pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. \since 0.5 */ - int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */ - uint32_t queue_length; /**< Queue size in bytes. For both playback and record streams. */ - int synchronized_clocks; /**< Non-zero if the local and the - * remote machine have synchronized - * clocks. If synchronized clocks are - * detected transport_usec becomes much - * more reliable. However, the code that - * detects synchronized clocks is very - * limited und unreliable itself. \since - * 0.5 */ - struct timeval timestamp; /**< The time when this latency info was current */ - uint64_t counter; /**< The byte counter current when the latency info was requested. \since 0.6 */ -} pa_latency_info; - -/** A structure for the spawn api. This may be used to integrate auto - * spawned daemons into your application. For more information see - * pa_context_connect(). When spawning a new child process the - * waitpid() is used on the child's PID. The spawn routine will not - * block or ignore SIGCHLD signals, since this cannot be done in a - * thread compatible way. You might have to do this in - * prefork/postfork. \since 0.4 */ -typedef struct pa_spawn_api { - void (*prefork)(void); /**< Is called just before the fork in the parent process. May be NULL. */ - void (*postfork)(void); /**< Is called immediately after the fork in the parent process. May be NULL.*/ - void (*atfork)(void); /**< Is called immediately after the - * fork in the child process. May be - * NULL. It is not safe to close all - * file descriptors in this function - * unconditionally, since a UNIX socket - * (created using socketpair()) is - * passed to the new process. */ -} pa_spawn_api; - -PA_C_DECL_END - -#endif diff --git a/polyp/polyplib-error.c b/polyp/polyplib-error.c deleted file mode 100644 index 50a67270..00000000 --- a/polyp/polyplib-error.c +++ /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 -#endif - -#include -#include - -#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-error.h b/polyp/polyplib-error.h deleted file mode 100644 index dbbbf006..00000000 --- a/polyp/polyplib-error.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef foopolypliberrorhfoo -#define foopolypliberrorhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include "cdecl.h" - -/** \file - * Error management */ - -PA_C_DECL_BEGIN - -/** Return a human readable error message for the specified numeric error code */ -const char* pa_strerror(uint32_t error); - -PA_C_DECL_END - -#endif diff --git a/polyp/polyplib-internal.h b/polyp/polyplib-internal.h deleted file mode 100644 index 29596069..00000000 --- a/polyp/polyplib-internal.h +++ /dev/null @@ -1,154 +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; - pa_mainloop_api* mainloop; - - pa_socket_client *client; - pa_pstream *pstream; - pa_pdispatch *pdispatch; - - pa_dynarray *record_streams, *playback_streams; - PA_LLIST_HEAD(pa_stream, streams); - PA_LLIST_HEAD(pa_operation, operations); - - uint32_t ctag; - uint32_t error; - pa_context_state_t state; - - void (*state_callback)(pa_context*c, void *userdata); - void *state_userdata; - - void (*subscribe_callback)(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata); - void *subscribe_userdata; - - pa_memblock_stat *memblock_stat; - - int local; - int do_autospawn; - int autospawn_lock_fd; - pa_spawn_api spawn_api; - - pa_strlist *server_list; - - char *server; - - pa_client_conf *conf; -}; - -struct pa_stream { - int ref; - pa_context *context; - pa_mainloop_api *mainloop; - PA_LLIST_FIELDS(pa_stream); - - char *name; - pa_buffer_attr buffer_attr; - pa_sample_spec sample_spec; - pa_channel_map channel_map; - uint32_t channel; - int channel_valid; - uint32_t device_index; - pa_stream_direction_t direction; - uint32_t requested_bytes; - uint64_t counter; - pa_usec_t previous_time; - pa_usec_t previous_ipol_time; - pa_stream_state_t state; - pa_mcalign *mcalign; - - int interpolate; - int corked; - - uint32_t ipol_usec; - struct timeval ipol_timestamp; - pa_time_event *ipol_event; - int ipol_requested; - - void (*state_callback)(pa_stream*c, void *userdata); - void *state_userdata; - - void (*read_callback)(pa_stream *p, const void*data, size_t length, void *userdata); - void *read_userdata; - - void (*write_callback)(pa_stream *p, size_t length, void *userdata); - void *write_userdata; -}; - -typedef void (*pa_operation_callback)(void); - -struct pa_operation { - int ref; - pa_context *context; - pa_stream *stream; - PA_LLIST_FIELDS(pa_operation); - - pa_operation_state_t state; - void *userdata; - pa_operation_callback callback; -}; - -void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); - -pa_operation *pa_operation_new(pa_context *c, pa_stream *s); -void pa_operation_done(pa_operation *o); - -void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); - -void pa_context_fail(pa_context *c, int error); -void pa_context_set_state(pa_context *c, pa_context_state_t st); -int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t); -pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata); - -void pa_stream_set_state(pa_stream *s, pa_stream_state_t st); - -void pa_stream_trash_ipol(pa_stream *s); - - -#endif diff --git a/polyp/polyplib-introspect.c b/polyp/polyplib-introspect.c deleted file mode 100644 index 49298a96..00000000 --- a/polyp/polyplib-introspect.c +++ /dev/null @@ -1,1003 +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 -#endif - -#include - -#include "polyplib-introspect.h" -#include "polyplib-context.h" -#include "polyplib-internal.h" -#include "pstream-util.h" -#include "gccmacro.h" - -/*** Statistics ***/ - -static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - pa_stat_info i, *p = &i; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - p = NULL; - } else if (pa_tagstruct_getu32(t, &i.memblock_total) < 0 || - pa_tagstruct_getu32(t, &i.memblock_total_size) < 0 || - pa_tagstruct_getu32(t, &i.memblock_allocated) < 0 || - pa_tagstruct_getu32(t, &i.memblock_allocated_size) < 0 || - pa_tagstruct_getu32(t, &i.scache_size) < 0 || - !pa_tagstruct_eof(t)) { - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_stat_info*_i, void *_userdata) = (void (*)(pa_context *s, const pa_stat_info*_i, void *_userdata)) o->callback; - cb(o->context, p, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_stat(pa_context *c, void (*cb)(pa_context *c, const pa_stat_info*i, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_STAT, context_stat_callback, (pa_operation_callback) cb, userdata); -} - -/*** Server Info ***/ - -static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - pa_server_info i, *p = &i; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - p = NULL; - } else if (pa_tagstruct_gets(t, &i.server_name) < 0 || - pa_tagstruct_gets(t, &i.server_version) < 0 || - pa_tagstruct_gets(t, &i.user_name) < 0 || - pa_tagstruct_gets(t, &i.host_name) < 0 || - pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || - pa_tagstruct_gets(t, &i.default_sink_name) < 0 || - pa_tagstruct_gets(t, &i.default_source_name) < 0 || - pa_tagstruct_getu32(t, &i.cookie) < 0 || - !pa_tagstruct_eof(t)) { - - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_server_info*_i, void *_userdata) = (void (*)(pa_context *s, const pa_server_info*_i, void *_userdata)) o->callback; - cb(o->context, p, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_get_server_info(pa_context *c, void (*cb)(pa_context *c, const pa_server_info*i, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_SERVER_INFO, context_get_server_info_callback, (pa_operation_callback) cb, userdata); -} - -/*** Sink Info ***/ - -static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - int eof = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - eof = -1; - } else { - - while (!pa_tagstruct_eof(t)) { - pa_sink_info i; - - if (pa_tagstruct_getu32(t, &i.index) < 0 || - pa_tagstruct_gets(t, &i.name) < 0 || - pa_tagstruct_gets(t, &i.description) < 0 || - pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || - pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || - pa_tagstruct_getu32(t, &i.owner_module) < 0 || - pa_tagstruct_get_cvolume(t, &i.volume) < 0 || - pa_tagstruct_getu32(t, &i.monitor_source) < 0 || - pa_tagstruct_gets(t, &i.monitor_source_name) < 0 || - pa_tagstruct_get_usec(t, &i.latency) < 0 || - pa_tagstruct_gets(t, &i.driver) < 0) { - - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, NULL, eof, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_get_sink_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INFO_LIST, context_get_sink_info_callback, (pa_operation_callback) cb, userdata); -} - -pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, idx); - pa_tagstruct_puts(t, NULL); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_info_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, PA_INVALID_INDEX); - pa_tagstruct_puts(t, name); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_info_callback, o); - - return pa_operation_ref(o); -} - -/*** Source info ***/ - -static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - int eof = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - eof = -1; - } else { - - while (!pa_tagstruct_eof(t)) { - pa_source_info i; - - if (pa_tagstruct_getu32(t, &i.index) < 0 || - pa_tagstruct_gets(t, &i.name) < 0 || - pa_tagstruct_gets(t, &i.description) < 0 || - pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || - pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || - pa_tagstruct_getu32(t, &i.owner_module) < 0 || - pa_tagstruct_getu32(t, &i.monitor_of_sink) < 0 || - pa_tagstruct_gets(t, &i.monitor_of_sink_name) < 0 || - pa_tagstruct_get_usec(t, &i.latency) < 0 || - pa_tagstruct_gets(t, &i.driver) < 0) { - - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, NULL, eof, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_get_source_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_INFO_LIST, context_get_source_info_callback, (pa_operation_callback) cb, userdata); -} - -pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, idx); - pa_tagstruct_puts(t, NULL); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_info_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, PA_INVALID_INDEX); - pa_tagstruct_puts(t, name); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_info_callback, o); - - return pa_operation_ref(o); -} - -/*** Client info ***/ - -static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - int eof = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - eof = -1; - } else { - - while (!pa_tagstruct_eof(t)) { - pa_client_info i; - - if (pa_tagstruct_getu32(t, &i.index) < 0 || - pa_tagstruct_gets(t, &i.name) < 0 || - pa_tagstruct_getu32(t, &i.owner_module) < 0 || - pa_tagstruct_gets(t, &i.driver) < 0 ) { - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, NULL, eof, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_CLIENT_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, idx); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_client_info_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_client_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_CLIENT_INFO_LIST, context_get_client_info_callback, (pa_operation_callback) cb, userdata); -} - -/*** Module info ***/ - -static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - int eof = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - eof = -1; - } else { - - while (!pa_tagstruct_eof(t)) { - pa_module_info i; - - if (pa_tagstruct_getu32(t, &i.index) < 0 || - pa_tagstruct_gets(t, &i.name) < 0 || - pa_tagstruct_gets(t, &i.argument) < 0 || - pa_tagstruct_getu32(t, &i.n_used) < 0 || - pa_tagstruct_get_boolean(t, &i.auto_unload) < 0) { - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, NULL, eof, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_MODULE_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, idx); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_module_info_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_module_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_MODULE_INFO_LIST, context_get_module_info_callback, (pa_operation_callback) cb, userdata); -} - -/*** Sink input info ***/ - -static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - int eof = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - eof = -1; - } else { - - while (!pa_tagstruct_eof(t)) { - pa_sink_input_info i; - - if (pa_tagstruct_getu32(t, &i.index) < 0 || - pa_tagstruct_gets(t, &i.name) < 0 || - pa_tagstruct_getu32(t, &i.owner_module) < 0 || - pa_tagstruct_getu32(t, &i.client) < 0 || - pa_tagstruct_getu32(t, &i.sink) < 0 || - pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || - pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || - pa_tagstruct_get_cvolume(t, &i.volume) < 0 || - pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 || - pa_tagstruct_get_usec(t, &i.sink_usec) < 0 || - pa_tagstruct_gets(t, &i.resample_method) < 0 || - pa_tagstruct_gets(t, &i.driver) < 0) { - - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, NULL, eof, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INPUT_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, idx); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_input_info_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_sink_input_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INPUT_INFO_LIST, context_get_sink_input_info_callback, (pa_operation_callback) cb, userdata); -} - -/*** Source output info ***/ - -static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - int eof = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - eof = -1; - } else { - - while (!pa_tagstruct_eof(t)) { - pa_source_output_info i; - - if (pa_tagstruct_getu32(t, &i.index) < 0 || - pa_tagstruct_gets(t, &i.name) < 0 || - pa_tagstruct_getu32(t, &i.owner_module) < 0 || - pa_tagstruct_getu32(t, &i.client) < 0 || - pa_tagstruct_getu32(t, &i.source) < 0 || - pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || - pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || - pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 || - pa_tagstruct_get_usec(t, &i.source_usec) < 0 || - pa_tagstruct_gets(t, &i.resample_method) < 0 || - pa_tagstruct_gets(t, &i.driver) < 0) { - - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata))o->callback; - cb(o->context, NULL, eof, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_OUTPUT_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, idx); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_output_info_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_source_output_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST, context_get_source_output_info_callback, (pa_operation_callback) cb, userdata); -} - -/*** Volume manipulation ***/ - -pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && idx != PA_INVALID_INDEX); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_VOLUME); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, idx); - pa_tagstruct_puts(t, NULL); - pa_tagstruct_put_cvolume(t, volume); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && name); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_VOLUME); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, PA_INVALID_INDEX); - pa_tagstruct_puts(t, name); - pa_tagstruct_put_cvolume(t, volume); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && idx != PA_INVALID_INDEX); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, idx); - pa_tagstruct_put_cvolume(t, volume); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); - - return pa_operation_ref(o); -} - -/** Sample Cache **/ - -static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - int eof = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - eof = -1; - } else { - - while (!pa_tagstruct_eof(t)) { - pa_sample_info i; - - if (pa_tagstruct_getu32(t, &i.index) < 0 || - pa_tagstruct_gets(t, &i.name) < 0 || - pa_tagstruct_get_cvolume(t, &i.volume) < 0 || - pa_tagstruct_get_usec(t, &i.duration) < 0 || - pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || - pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || - pa_tagstruct_getu32(t, &i.bytes) < 0 || - pa_tagstruct_get_boolean(t, &i.lazy) < 0 || - pa_tagstruct_gets(t, &i.filename) < 0) { - - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, NULL, eof, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb && name); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SAMPLE_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, PA_INVALID_INDEX); - pa_tagstruct_puts(t, name); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sample_info_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_SAMPLE_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, idx); - pa_tagstruct_puts(t, NULL); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sample_info_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_sample_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_SAMPLE_INFO_LIST, context_get_sample_info_callback, (pa_operation_callback) cb, userdata); -} - -static pa_operation* command_kill(pa_context *c, uint32_t command, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && idx != PA_INVALID_INDEX); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, command); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, idx); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - return command_kill(c, PA_COMMAND_KILL_CLIENT, idx, cb, userdata); -} - -pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - return command_kill(c, PA_COMMAND_KILL_SINK_INPUT, idx, cb, userdata); -} - -pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - return command_kill(c, PA_COMMAND_KILL_SOURCE_OUTPUT, idx, cb, userdata); -} - -static void load_module_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - uint32_t idx = -1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - } else if (pa_tagstruct_getu32(t, &idx) < 0 || - !pa_tagstruct_eof(t)) { - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *c, uint32_t _idx, void *_userdata) = (void (*)(pa_context *c, uint32_t _idx, void *_userdata)) o->callback; - cb(o->context, idx, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, void (*cb)(pa_context *c, uint32_t idx, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && name && argument); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_LOAD_MODULE); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_puts(t, name); - pa_tagstruct_puts(t, argument); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, load_module_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - return command_kill(c, PA_COMMAND_UNLOAD_MODULE, idx, cb, userdata); -} - -/*** Autoload stuff ***/ - -static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - int eof = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - eof = -1; - } else { - - while (!pa_tagstruct_eof(t)) { - pa_autoload_info i; - - if (pa_tagstruct_getu32(t, &i.index) < 0 || - pa_tagstruct_gets(t, &i.name) < 0 || - pa_tagstruct_getu32(t, &i.type) < 0 || - pa_tagstruct_gets(t, &i.module) < 0 || - pa_tagstruct_gets(t, &i.argument) < 0) { - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, &i, 0, o->userdata); - } - } - } - - if (o->callback) { - void (*cb)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata)) o->callback; - cb(o->context, NULL, eof, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb && name); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_AUTOLOAD_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_puts(t, name); - pa_tagstruct_putu32(t, type); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(c && cb && idx != PA_INVALID_INDEX); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_GET_AUTOLOAD_INFO); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, idx); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_get_autoload_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata) { - return pa_context_send_simple_command(c, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, context_get_autoload_info_callback, (pa_operation_callback) cb, userdata); -} - -static void context_add_autoload_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - uint32_t idx; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - idx = PA_INVALID_INDEX; - } else if (pa_tagstruct_getu32(t, &idx) || - !pa_tagstruct_eof(t)) { - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_context *s, uint32_t _idx, void *_userdata) = (void (*)(pa_context *s, uint32_t _idx, void *_userdata)) o->callback; - cb(o->context, idx, o->userdata); - } - - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, void (*cb)(pa_context *c, int success, void *userdata), void* userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && name && module && argument); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_ADD_AUTOLOAD); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_puts(t, name); - pa_tagstruct_putu32(t, type); - pa_tagstruct_puts(t, module); - pa_tagstruct_puts(t, argument); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_add_autoload_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, void (*cb)(pa_context *c, int success, void *userdata), void* userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && name); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_AUTOLOAD); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_puts(t, name); - pa_tagstruct_putu32(t, type); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void* userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && idx != PA_INVALID_INDEX); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_AUTOLOAD); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, idx); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); - - return pa_operation_ref(o); -} diff --git a/polyp/polyplib-introspect.h b/polyp/polyplib-introspect.h deleted file mode 100644 index d3489908..00000000 --- a/polyp/polyplib-introspect.h +++ /dev/null @@ -1,279 +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 - -#include -#include -#include -#include -#include - -/** \file - * - * Routines for daemon introspection. When enumerating all entitites - * of a certain kind, use the pa_context_xxx_list() functions. The - * specified callback function is called once for each entry. The - * enumeration is finished by a call to the callback function with - * is_last=1 and i=NULL. Strings referenced in pa_xxx_info structures - * and the structures themselves point to internal memory that may not - * be modified. That memory is only valid during the call to the - * callback function. A deep copy is required if you need this data - * outside the callback functions. An error is signalled by a call to * the callback function with i=NULL and is_last=0. - * - * When using the routines that ask fo a single entry only, a callback - * with the same signature is used. However, no finishing call to the - * routine is issued. */ - -PA_C_DECL_BEGIN - -/** Stores information about sinks */ -typedef struct pa_sink_info { - const char *name; /**< Name of the sink */ - uint32_t index; /**< Index of the sink */ - const char *description; /**< Description of this sink */ - pa_sample_spec sample_spec; /**< Sample spec of this sink */ - pa_channel_map channel_map; /**< Channel map \since 0.9 */ - uint32_t owner_module; /**< Index of the owning module of this sink, or PA_INVALID_INDEX */ - pa_cvolume volume; /**< Volume of the sink */ - uint32_t monitor_source; /**< Index of the monitor source connected to this sink */ - const char *monitor_source_name; /**< The name of the monitor source */ - pa_usec_t latency; /**< Length of filled playback buffer of this sink */ - const char *driver; /**< Driver name. \since 0.9 */ -} pa_sink_info; - -/** Get information about a sink by its name */ -pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata); - -/** Get information about a sink by its index */ -pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t id, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata); - -/** Get the complete sink list */ -pa_operation* pa_context_get_sink_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata); - -/** Stores information about sources */ -typedef struct pa_source_info { - const char *name ; /**< Name of the source */ - uint32_t index; /**< Index of the source */ - const char *description; /**< Description of this source */ - pa_sample_spec sample_spec; /**< Sample spec of this source */ - pa_channel_map channel_map; /**< Channel map \since 0.9 */ - uint32_t owner_module; /**< Owning module index, or PA_INVALID_INDEX */ - uint32_t monitor_of_sink; /**< If this is a monitor source the index of the owning sink, otherwise PA_INVALID_INDEX */ - const char *monitor_of_sink_name; /**< Name of the owning sink, or PA_INVALID_INDEX */ - pa_usec_t latency; /**< Length of filled record buffer of this source. \since 0.5 */ - const char *driver; /**< Driver name \since 0.9 */ -} pa_source_info; - -/** Get information about a source by its name */ -pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata); - -/** Get information about a source by its index */ -pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t id, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata); - -/** Get the complete source list */ -pa_operation* pa_context_get_source_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata); - -/** Server information */ -typedef struct pa_server_info { - const char *user_name; /**< User name of the daemon process */ - const char *host_name; /**< Host name the daemon is running on */ - const char *server_version; /**< Version string of the daemon */ - const char *server_name; /**< Server package name (usually "polypaudio") */ - pa_sample_spec sample_spec; /**< Default sample specification */ - const char *default_sink_name; /**< Name of default sink. \since 0.4 */ - const char *default_source_name; /**< Name of default sink. \since 0.4*/ - uint32_t cookie; /**< A random cookie for identifying this instance of polypaudio. \since 0.8 */ -} pa_server_info; - -/** Get some information about the server */ -pa_operation* pa_context_get_server_info(pa_context *c, void (*cb)(pa_context *c, const pa_server_info*i, void *userdata), void *userdata); - -/** Stores information about modules */ -typedef struct pa_module_info { - uint32_t index; /**< Index of the module */ - const char*name, /**< Name of the module */ - *argument; /**< Argument string of the module */ - uint32_t n_used; /**< Usage counter or PA_INVALID_INDEX */ - int auto_unload; /**< Non-zero if this is an autoloaded module */ -} pa_module_info; - -/** Get some information about a module by its index */ -pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata); - -/** Get the complete list of currently loaded modules */ -pa_operation* pa_context_get_module_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata); - -/** Stores information about clients */ -typedef struct pa_client_info { - uint32_t index; /**< Index of this client */ - const char *name; /**< Name of this client */ - uint32_t owner_module; /**< Index of the owning module, or PA_INVALID_INDEX */ - const char *driver; /**< Driver name \since 0.9 */ -} pa_client_info; - -/** Get information about a client by its index */ -pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata); - -/** Get the complete client list */ -pa_operation* pa_context_get_client_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata); - -/** Stores information about sink inputs */ -typedef struct pa_sink_input_info { - uint32_t index; /**< Index of the sink input */ - const char *name; /**< Name of the sink input */ - uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */ - uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */ - uint32_t sink; /**< Index of the connected sink */ - pa_sample_spec sample_spec; /**< The sample specification of the sink input */ - pa_channel_map channel_map; /**< Channel map */ - pa_cvolume volume; /**< The volume of this sink input */ - pa_usec_t buffer_usec; /**< Latency due to buffering in sink input, see pa_latency_info for details */ - pa_usec_t sink_usec; /**< Latency of the sink device, see pa_latency_info for details */ - const char *resample_method; /**< Thre resampling method used by this sink input. \since 0.7 */ - const char *driver; /**< Driver name \since 0.9 */ -} pa_sink_input_info; - -/** Get some information about a sink input by its index */ -pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata); - -/** Get the complete sink input list */ -pa_operation* pa_context_get_sink_input_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata); - -/** Stores information about source outputs */ -typedef struct pa_source_output_info { - uint32_t index; /**< Index of the sink input */ - const char *name; /**< Name of the sink input */ - uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */ - uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */ - uint32_t source; /**< Index of the connected source */ - pa_sample_spec sample_spec; /**< The sample specification of the source output */ - pa_channel_map channel_map; /**< Channel map */ - pa_usec_t buffer_usec; /**< Latency due to buffering in the source output, see pa_latency_info for details. \since 0.5 */ - pa_usec_t source_usec; /**< Latency of the source device, see pa_latency_info for details. \since 0.5 */ - const char *resample_method; /**< Thre resampling method used by this source output. \since 0.7 */ - const char *driver; /**< Driver name \since 0.9 */ -} pa_source_output_info; - -/** Get information about a source output by its index */ -pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata); - -/** Get the complete list of source outputs */ -pa_operation* pa_context_get_source_output_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata); - -/** Set the volume of a sink device specified by its index */ -pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); - -/** Set the volume of a sink device specified by its name */ -pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); - -/** Set the volume of a sink input stream */ -pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); - -/** Memory block statistics */ -typedef struct pa_stat_info { - uint32_t memblock_total; /**< Currently allocated memory blocks */ - uint32_t memblock_total_size; /**< Currentl total size of allocated memory blocks */ - uint32_t memblock_allocated; /**< Allocated memory blocks during the whole lifetime of the daemon */ - uint32_t memblock_allocated_size; /**< Total size of all memory blocks allocated during the whole lifetime of the daemon */ - uint32_t scache_size; /**< Total size of all sample cache entries. \since 0.4 */ -} pa_stat_info; - -/** Get daemon memory block statistics */ -pa_operation* pa_context_stat(pa_context *c, void (*cb)(pa_context *c, const pa_stat_info *i, void *userdata), void *userdata); - -/** Stores information about sample cache entries */ -typedef struct pa_sample_info { - uint32_t index; /**< Index of this entry */ - const char *name; /**< Name of this entry */ - pa_cvolume volume; /**< Default volume of this entry */ - pa_sample_spec sample_spec; /**< Sample specification of the sample */ - pa_channel_map channel_map; /**< The channel map */ - pa_usec_t duration; /**< Duration of this entry */ - uint32_t bytes; /**< Length of this sample in bytes. \since 0.4 */ - int lazy; /**< Non-zero when this is a lazy cache entry. \since 0.5 */ - const char *filename; /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. \since 0.5 */ -} pa_sample_info; - -/** Get information about a sample by its name */ -pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata); - -/** Get information about a sample by its index */ -pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata); - -/** Get the complete list of samples stored in the daemon. */ -pa_operation* pa_context_get_sample_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata); - -/** Kill a client. \since 0.5 */ -pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); - -/** Kill a sink input. \since 0.5 */ -pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); - -/** Kill a source output. \since 0.5 */ -pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); - -/** Load a module. \since 0.5 */ -pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, void (*cb)(pa_context *c, uint32_t idx, void *userdata), void *userdata); - -/** Unload a module. \since 0.5 */ -pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); - -/** Type of an autoload entry. \since 0.5 */ -typedef enum pa_autoload_type { - PA_AUTOLOAD_SINK = 0, - PA_AUTOLOAD_SOURCE = 1 -} pa_autoload_type_t; - -/** Stores information about autoload entries. \since 0.5 */ -typedef struct pa_autoload_info { - uint32_t index; /**< Index of this autoload entry */ - const char *name; /**< Name of the sink or source */ - pa_autoload_type_t type; /**< Type of the autoload entry */ - const char *module; /**< Module name to load */ - const char *argument; /**< Argument string for module */ -} pa_autoload_info; - -/** Get info about a specific autoload entry. \since 0.6 */ -pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata); - -/** Get info about a specific autoload entry. \since 0.6 */ -pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata); - -/** Get the complete list of autoload entries. \since 0.5 */ -pa_operation* pa_context_get_autoload_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata); - -/** Add a new autoload entry. \since 0.5 */ -pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, void (*cb)(pa_context *c, int idx, void *userdata), void* userdata); - -/** Remove an autoload entry. \since 0.6 */ -pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, void (*cb)(pa_context *c, int success, void *userdata), void* userdata); - -/** Remove an autoload entry. \since 0.6 */ -pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void* userdata); - - -PA_C_DECL_END - -#endif diff --git a/polyp/polyplib-operation.c b/polyp/polyplib-operation.c deleted file mode 100644 index 68bc8c6a..00000000 --- a/polyp/polyplib-operation.c +++ /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 -#endif - -#include - -#include "xmalloc.h" -#include "polyplib-internal.h" -#include "polyplib-operation.h" - -pa_operation *pa_operation_new(pa_context *c, pa_stream *s) { - pa_operation *o; - assert(c); - - o = pa_xmalloc(sizeof(pa_operation)); - o->ref = 1; - o->context = pa_context_ref(c); - o->stream = s ? pa_stream_ref(s) : NULL; - - o->state = PA_OPERATION_RUNNING; - o->userdata = NULL; - o->callback = NULL; - - PA_LLIST_PREPEND(pa_operation, o->context->operations, o); - return pa_operation_ref(o); -} - -pa_operation *pa_operation_ref(pa_operation *o) { - assert(o && o->ref >= 1); - o->ref++; - return o; -} - -void pa_operation_unref(pa_operation *o) { - assert(o && o->ref >= 1); - - if ((--(o->ref)) == 0) { - assert(!o->context); - assert(!o->stream); - free(o); - } -} - -static void operation_set_state(pa_operation *o, pa_operation_state_t st) { - assert(o && o->ref >= 1); - - if (st == o->state) - return; - - if (!o->context) - return; - - o->state = st; - - if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) { - PA_LLIST_REMOVE(pa_operation, o->context->operations, o); - pa_context_unref(o->context); - if (o->stream) - pa_stream_unref(o->stream); - o->context = NULL; - o->stream = NULL; - o->callback = NULL; - o->userdata = NULL; - - pa_operation_unref(o); - } -} - -void pa_operation_cancel(pa_operation *o) { - assert(o && o->ref >= 1); - operation_set_state(o, PA_OPERATION_CANCELED); -} - -void pa_operation_done(pa_operation *o) { - assert(o && o->ref >= 1); - operation_set_state(o, PA_OPERATION_DONE); -} - -pa_operation_state_t pa_operation_get_state(pa_operation *o) { - assert(o && o->ref >= 1); - return o->state; -} diff --git a/polyp/polyplib-operation.h b/polyp/polyplib-operation.h deleted file mode 100644 index cac03e30..00000000 --- a/polyp/polyplib-operation.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef foopolypliboperationhfoo -#define foopolypliboperationhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include - -/** \file - * Asynchronous operations */ - -PA_C_DECL_BEGIN - -/** \pa_operation - * An asynchronous operation object */ -typedef struct pa_operation pa_operation; - -/** Increase the reference count by one */ -pa_operation *pa_operation_ref(pa_operation *o); - -/** Decrease the reference count by one */ -void pa_operation_unref(pa_operation *o); - -/** Cancel the operation. Beware! This will not necessarily cancel the execution of the operation on the server side. */ -void pa_operation_cancel(pa_operation *o); - -/** Return the current status of the operation */ -pa_operation_state_t pa_operation_get_state(pa_operation *o); - -PA_C_DECL_END - -#endif diff --git a/polyp/polyplib-scache.c b/polyp/polyplib-scache.c deleted file mode 100644 index 01c64828..00000000 --- a/polyp/polyplib-scache.c +++ /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 -#endif - -#include -#include -#include -#include - -#include "polyplib-scache.h" -#include "polyplib-internal.h" -#include "pstream-util.h" - -void pa_stream_connect_upload(pa_stream *s, size_t length) { - pa_tagstruct *t; - uint32_t tag; - - assert(s && length); - - pa_stream_ref(s); - - s->state = PA_STREAM_CREATING; - s->direction = PA_STREAM_UPLOAD; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_CREATE_UPLOAD_STREAM); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - pa_tagstruct_puts(t, s->name); - pa_tagstruct_put_sample_spec(t, &s->sample_spec); - pa_tagstruct_putu32(t, length); - pa_pstream_send_tagstruct(s->context->pstream, t); - pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s); - - pa_stream_unref(s); -} - -void pa_stream_finish_upload(pa_stream *s) { - pa_tagstruct *t; - uint32_t tag; - assert(s); - - if (!s->channel_valid || !s->context->state == PA_CONTEXT_READY) - return; - - pa_stream_ref(s); - - t = pa_tagstruct_new(NULL, 0); - assert(t); - - pa_tagstruct_putu32(t, PA_COMMAND_FINISH_UPLOAD_STREAM); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - pa_tagstruct_putu32(t, s->channel); - pa_pstream_send_tagstruct(s->context->pstream, t); - pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s); - - pa_stream_unref(s); -} - -pa_operation * pa_context_play_sample(pa_context *c, const char *name, const char *dev, uint32_t volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && name && *name && (!dev || *dev)); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - if (!dev) - dev = c->conf->default_sink; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_PLAY_SAMPLE); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, (uint32_t) -1); - pa_tagstruct_puts(t, dev); - pa_tagstruct_putu32(t, volume); - pa_tagstruct_puts(t, name); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_context_remove_sample(pa_context *c, const char *name, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c && name); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_SAMPLE); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_puts(t, name); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); - - return pa_operation_ref(o); -} - diff --git a/polyp/polyplib-scache.h b/polyp/polyplib-scache.h deleted file mode 100644 index 89d27597..00000000 --- a/polyp/polyplib-scache.h +++ /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 - -#include -#include -#include - -/** \file - * All sample cache related routines */ - -PA_C_DECL_BEGIN - -/** Make this stream a sample upload stream */ -void pa_stream_connect_upload(pa_stream *s, size_t length); - -/** Finish the sample upload, the stream name will become the sample name. You cancel a sample upload by issuing pa_stream_disconnect() */ -void pa_stream_finish_upload(pa_stream *s); - -/** Play a sample from the sample cache to the specified device. If the latter is NULL use the default sink. Returns an operation object */ -pa_operation* pa_context_play_sample(pa_context *c, const char *name, const char *dev, uint32_t volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); - -/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */ -pa_operation* pa_context_remove_sample(pa_context *c, const char *name, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); - -PA_C_DECL_END - -#endif diff --git a/polyp/polyplib-simple.c b/polyp/polyplib-simple.c deleted file mode 100644 index e5512152..00000000 --- a/polyp/polyplib-simple.c +++ /dev/null @@ -1,393 +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 -#endif - -#include -#include -#include -#include - -#include "polyplib-simple.h" -#include "polyplib.h" -#include "mainloop.h" -#include "native-common.h" -#include "xmalloc.h" -#include "log.h" - -struct pa_simple { - pa_mainloop *mainloop; - pa_context *context; - pa_stream *stream; - pa_stream_direction_t direction; - - int dead; - - void *read_data; - size_t read_index, read_length; - pa_usec_t latency; -}; - -static void read_callback(pa_stream *s, const void*data, size_t length, void *userdata); - -static int check_error(pa_simple *p, int *rerror) { - pa_context_state_t cst; - pa_stream_state_t sst; - assert(p); - - if ((cst = pa_context_get_state(p->context)) == PA_CONTEXT_FAILED) - goto fail; - - assert(cst != PA_CONTEXT_TERMINATED); - - if (p->stream) { - if ((sst = pa_stream_get_state(p->stream)) == PA_STREAM_FAILED) - goto fail; - - assert(sst != PA_STREAM_TERMINATED); - } - - return 0; - -fail: - if (rerror) - *rerror = pa_context_errno(p->context); - - p->dead = 1; - - return -1; -} - -static int iterate(pa_simple *p, int block, int *rerror) { - assert(p && p->context && p->mainloop); - - if (check_error(p, rerror) < 0) - return -1; - - if (!block && !pa_context_is_pending(p->context)) - return 0; - - do { - if (pa_mainloop_iterate(p->mainloop, 1, NULL) < 0) { - if (rerror) - *rerror = PA_ERROR_INTERNAL; - return -1; - } - - if (check_error(p, rerror) < 0) - return -1; - - } while (pa_context_is_pending(p->context)); - - - while (pa_mainloop_deferred_pending(p->mainloop)) { - - if (pa_mainloop_iterate(p->mainloop, 0, NULL) < 0) { - if (rerror) - *rerror = PA_ERROR_INTERNAL; - return -1; - } - - if (check_error(p, rerror) < 0) - return -1; - } - - return 0; -} - -pa_simple* pa_simple_new( - const char *server, - const char *name, - pa_stream_direction_t dir, - const char *dev, - const char *stream_name, - const pa_sample_spec *ss, - const pa_buffer_attr *attr, - int *rerror) { - - pa_simple *p; - int error = PA_ERROR_INTERNAL; - assert(ss && (dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD)); - - p = pa_xmalloc(sizeof(pa_simple)); - p->context = NULL; - p->stream = NULL; - p->mainloop = pa_mainloop_new(); - assert(p->mainloop); - p->dead = 0; - p->direction = dir; - p->read_data = NULL; - p->read_index = p->read_length = 0; - p->latency = 0; - - if (!(p->context = pa_context_new(pa_mainloop_get_api(p->mainloop), name))) - goto fail; - - pa_context_connect(p->context, server, 1, NULL); - - /* Wait until the context is ready */ - while (pa_context_get_state(p->context) != PA_CONTEXT_READY) { - if (iterate(p, 1, &error) < 0) - goto fail; - } - - if (!(p->stream = pa_stream_new(p->context, stream_name, ss, NULL))) - goto fail; - - if (dir == PA_STREAM_PLAYBACK) - pa_stream_connect_playback(p->stream, dev, attr, 0, NULL); - else - pa_stream_connect_record(p->stream, dev, attr, 0); - - /* Wait until the stream is ready */ - while (pa_stream_get_state(p->stream) != PA_STREAM_READY) { - if (iterate(p, 1, &error) < 0) - goto fail; - } - - pa_stream_set_read_callback(p->stream, read_callback, p); - - return p; - -fail: - if (rerror) - *rerror = error; - pa_simple_free(p); - return NULL; -} - -void pa_simple_free(pa_simple *s) { - assert(s); - - pa_xfree(s->read_data); - - if (s->stream) - pa_stream_unref(s->stream); - - if (s->context) - pa_context_unref(s->context); - - if (s->mainloop) - pa_mainloop_free(s->mainloop); - - pa_xfree(s); -} - -int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) { - assert(p && data && p->direction == PA_STREAM_PLAYBACK); - - if (p->dead) { - if (rerror) - *rerror = pa_context_errno(p->context); - - return -1; - } - - while (length > 0) { - size_t l; - - while (!(l = pa_stream_writable_size(p->stream))) - if (iterate(p, 1, rerror) < 0) - return -1; - - if (l > length) - l = length; - - pa_stream_write(p->stream, data, l, NULL, 0); - data = (const uint8_t*) data + l; - length -= l; - } - - /* Make sure that no data is pending for write */ - if (iterate(p, 0, rerror) < 0) - return -1; - - return 0; -} - -static void read_callback(pa_stream *s, const void*data, size_t length, void *userdata) { - pa_simple *p = userdata; - assert(s && data && length && p); - - if (p->read_data) { - pa_log(__FILE__": Buffer overflow, dropping incoming memory blocks.\n"); - pa_xfree(p->read_data); - } - - p->read_data = pa_xmemdup(data, p->read_length = length); - p->read_index = 0; -} - -int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) { - assert(p && data && p->direction == PA_STREAM_RECORD); - - if (p->dead) { - if (rerror) - *rerror = pa_context_errno(p->context); - - return -1; - } - - while (length > 0) { - if (p->read_data) { - size_t l = length; - - if (p->read_length <= l) - l = p->read_length; - - memcpy(data, (uint8_t*) p->read_data+p->read_index, l); - - data = (uint8_t*) data + l; - length -= l; - - p->read_index += l; - p->read_length -= l; - - if (!p->read_length) { - pa_xfree(p->read_data); - p->read_data = NULL; - p->read_index = 0; - } - - if (!length) - return 0; - - assert(!p->read_data); - } - - if (iterate(p, 1, rerror) < 0) - return -1; - } - - return 0; -} - -static void drain_or_flush_complete(pa_stream *s, int success, void *userdata) { - pa_simple *p = userdata; - assert(s && p); - if (!success) - p->dead = 1; -} - -int pa_simple_drain(pa_simple *p, int *rerror) { - pa_operation *o; - assert(p && p->direction == PA_STREAM_PLAYBACK); - - if (p->dead) { - if (rerror) - *rerror = pa_context_errno(p->context); - - return -1; - } - - o = pa_stream_drain(p->stream, drain_or_flush_complete, p); - - while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { - if (iterate(p, 1, rerror) < 0) { - pa_operation_cancel(o); - pa_operation_unref(o); - return -1; - } - } - - pa_operation_unref(o); - - if (p->dead && rerror) - *rerror = pa_context_errno(p->context); - - return p->dead ? -1 : 0; -} - -static void latency_complete(pa_stream *s, const pa_latency_info *l, void *userdata) { - pa_simple *p = userdata; - assert(s && p); - - if (!l) - p->dead = 1; - else { - int negative = 0; - p->latency = pa_stream_get_latency(s, l, &negative); - if (negative) - p->latency = 0; - } -} - -pa_usec_t pa_simple_get_playback_latency(pa_simple *p, int *rerror) { - pa_operation *o; - assert(p && p->direction == PA_STREAM_PLAYBACK); - - if (p->dead) { - if (rerror) - *rerror = pa_context_errno(p->context); - - return (pa_usec_t) -1; - } - - p->latency = 0; - o = pa_stream_get_latency_info(p->stream, latency_complete, p); - - while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { - - if (iterate(p, 1, rerror) < 0) { - pa_operation_cancel(o); - pa_operation_unref(o); - return -1; - } - } - - pa_operation_unref(o); - - if (p->dead && rerror) - *rerror = pa_context_errno(p->context); - - return p->dead ? (pa_usec_t) -1 : p->latency; -} - -int pa_simple_flush(pa_simple *p, int *rerror) { - pa_operation *o; - assert(p && p->direction == PA_STREAM_PLAYBACK); - - if (p->dead) { - if (rerror) - *rerror = pa_context_errno(p->context); - - return -1; - } - - o = pa_stream_flush(p->stream, drain_or_flush_complete, p); - - while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { - if (iterate(p, 1, rerror) < 0) { - pa_operation_cancel(o); - pa_operation_unref(o); - return -1; - } - } - - pa_operation_unref(o); - - if (p->dead && rerror) - *rerror = pa_context_errno(p->context); - - return p->dead ? -1 : 0; -} diff --git a/polyp/polyplib-simple.h b/polyp/polyplib-simple.h deleted file mode 100644 index 0c80f460..00000000 --- a/polyp/polyplib-simple.h +++ /dev/null @@ -1,80 +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 - -#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 - -/** \pa_simple - * An opaque simple connection object */ -typedef struct pa_simple pa_simple; - -/** Create a new connection to the server */ -pa_simple* pa_simple_new( - const char *server, /**< Server name, or NULL for default */ - const char *name, /**< A descriptive name for this client (application name, ...) */ - pa_stream_direction_t dir, /**< Open this stream for recording or playback? */ - const char *dev, /**< Sink (resp. source) name, or NULL for default */ - const char *stream_name, /**< A descriptive name for this client (application name, song title, ...) */ - const pa_sample_spec *ss, /**< The sample type to use */ - const pa_buffer_attr *attr, /**< Buffering attributes, or NULL for default */ - int *error /**< A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here. */ - ); - -/** Close and free the connection to the server. The connection objects becomes invalid when this is called. */ -void pa_simple_free(pa_simple *s); - -/** Write some data to the server */ -int pa_simple_write(pa_simple *s, const void*data, size_t length, int *error); - -/** Wait until all data already written is played by the daemon */ -int pa_simple_drain(pa_simple *s, int *error); - -/** Read some data from the server */ -int pa_simple_read(pa_simple *s, void*data, size_t length, int *error); - -/** Return the playback latency. \since 0.5 */ -pa_usec_t pa_simple_get_playback_latency(pa_simple *s, int *error); - -/** Flush the playback buffer. \since 0.5 */ -int pa_simple_flush(pa_simple *s, int *error); - -PA_C_DECL_END - -#endif diff --git a/polyp/polyplib-stream.c b/polyp/polyplib-stream.c deleted file mode 100644 index 7b9d6863..00000000 --- a/polyp/polyplib-stream.c +++ /dev/null @@ -1,807 +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 -#endif - -#include -#include -#include -#include - -#include "polyplib-internal.h" -#include "xmalloc.h" -#include "pstream-util.h" -#include "util.h" -#include "log.h" - -#define LATENCY_IPOL_INTERVAL_USEC (10000L) - -pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) { - pa_stream *s; - assert(c); - assert(ss); - - if (!pa_sample_spec_valid(ss)) - return NULL; - - if (map && !pa_channel_map_valid(map)) - return NULL; - - s = pa_xnew(pa_stream, 1); - s->ref = 1; - s->context = c; - s->mainloop = c->mainloop; - - s->read_callback = NULL; - s->read_userdata = NULL; - s->write_callback = NULL; - s->write_userdata = NULL; - s->state_callback = NULL; - s->state_userdata = NULL; - - s->direction = PA_STREAM_NODIRECTION; - s->name = pa_xstrdup(name); - s->sample_spec = *ss; - - if (map) - s->channel_map = *map; - else - pa_channel_map_init_auto(&s->channel_map, ss->channels); - - s->channel = 0; - s->channel_valid = 0; - s->device_index = PA_INVALID_INDEX; - s->requested_bytes = 0; - s->state = PA_STREAM_DISCONNECTED; - memset(&s->buffer_attr, 0, sizeof(s->buffer_attr)); - - s->mcalign = pa_mcalign_new(pa_frame_size(ss), c->memblock_stat); - - s->counter = 0; - s->previous_time = 0; - s->previous_ipol_time = 0; - - s->corked = 0; - s->interpolate = 0; - - s->ipol_usec = 0; - memset(&s->ipol_timestamp, 0, sizeof(s->ipol_timestamp)); - s->ipol_event = NULL; - s->ipol_requested = 0; - - PA_LLIST_PREPEND(pa_stream, c->streams, s); - - return pa_stream_ref(s); -} - -static void stream_free(pa_stream *s) { - assert(s); - - if (s->ipol_event) { - assert(s->mainloop); - s->mainloop->time_free(s->ipol_event); - } - - pa_mcalign_free(s->mcalign); - - pa_xfree(s->name); - pa_xfree(s); -} - -void pa_stream_unref(pa_stream *s) { - assert(s && s->ref >= 1); - - if (--(s->ref) == 0) - stream_free(s); -} - -pa_stream* pa_stream_ref(pa_stream *s) { - assert(s && s->ref >= 1); - s->ref++; - return s; -} - -pa_stream_state_t pa_stream_get_state(pa_stream *s) { - assert(s && s->ref >= 1); - return s->state; -} - -pa_context* pa_stream_get_context(pa_stream *s) { - assert(s && s->ref >= 1); - return s->context; -} - -uint32_t pa_stream_get_index(pa_stream *s) { - assert(s && s->ref >= 1); - return s->device_index; -} - -void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { - assert(s && s->ref >= 1); - - if (s->state == st) - return; - - pa_stream_ref(s); - - s->state = st; - - if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED) && s->context) { - if (s->channel_valid) - pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL); - - PA_LLIST_REMOVE(pa_stream, s->context->streams, s); - pa_stream_unref(s); - } - - if (s->state_callback) - s->state_callback(s, s->state_userdata); - - pa_stream_unref(s); -} - -void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_context *c = userdata; - pa_stream *s; - uint32_t channel; - assert(pd && (command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED) && t && c); - - pa_context_ref(c); - - if (pa_tagstruct_getu32(t, &channel) < 0 || - !pa_tagstruct_eof(t)) { - pa_context_fail(c, PA_ERROR_PROTOCOL); - goto finish; - } - - if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel))) - goto finish; - - c->error = PA_ERROR_KILLED; - pa_stream_set_state(s, PA_STREAM_FAILED); - -finish: - pa_context_unref(c); -} - -void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_stream *s; - pa_context *c = userdata; - uint32_t bytes, channel; - assert(pd && command == PA_COMMAND_REQUEST && t && c); - - pa_context_ref(c); - - if (pa_tagstruct_getu32(t, &channel) < 0 || - pa_tagstruct_getu32(t, &bytes) < 0 || - !pa_tagstruct_eof(t)) { - pa_context_fail(c, PA_ERROR_PROTOCOL); - goto finish; - } - - if (!(s = pa_dynarray_get(c->playback_streams, channel))) - goto finish; - - if (s->state != PA_STREAM_READY) - goto finish; - - pa_stream_ref(s); - - s->requested_bytes += bytes; - - if (s->requested_bytes && s->write_callback) - s->write_callback(s, s->requested_bytes, s->write_userdata); - - pa_stream_unref(s); - -finish: - pa_context_unref(c); -} - -static void ipol_callback(pa_mainloop_api *m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { - struct timeval tv2; - pa_stream *s = userdata; - - pa_stream_ref(s); - -/* pa_log("requesting new ipol data\n"); */ - - if (s->state == PA_STREAM_READY && !s->ipol_requested) { - pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); - s->ipol_requested = 1; - } - - pa_gettimeofday(&tv2); - pa_timeval_add(&tv2, LATENCY_IPOL_INTERVAL_USEC); - - m->time_restart(e, &tv2); - - pa_stream_unref(s); -} - - -void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_stream *s = userdata; - assert(pd && s && s->state == PA_STREAM_CREATING); - - pa_stream_ref(s); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(s->context, command, t) < 0) - goto finish; - - pa_stream_set_state(s, PA_STREAM_FAILED); - goto finish; - } - - if (pa_tagstruct_getu32(t, &s->channel) < 0 || - ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->device_index) < 0) || - ((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0) || - !pa_tagstruct_eof(t)) { - pa_context_fail(s->context, PA_ERROR_PROTOCOL); - goto finish; - } - - s->channel_valid = 1; - pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s); - pa_stream_set_state(s, PA_STREAM_READY); - - if (s->interpolate) { - struct timeval tv; - pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); - - pa_gettimeofday(&tv); - tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */ - - assert(!s->ipol_event); - s->ipol_event = s->mainloop->time_new(s->mainloop, &tv, &ipol_callback, s); - } - - if (s->requested_bytes && s->ref > 1 && s->write_callback) - s->write_callback(s, s->requested_bytes, s->write_userdata); - -finish: - pa_stream_unref(s); -} - -static void create_stream(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags, const pa_cvolume *volume) { - pa_tagstruct *t; - uint32_t tag; - assert(s && s->ref >= 1 && s->state == PA_STREAM_DISCONNECTED); - - pa_stream_ref(s); - - s->interpolate = !!(flags & PA_STREAM_INTERPOLATE_LATENCY); - pa_stream_trash_ipol(s); - - if (attr) - s->buffer_attr = *attr; - else { - /* half a second */ - s->buffer_attr.tlength = pa_bytes_per_second(&s->sample_spec)/2; - s->buffer_attr.maxlength = (s->buffer_attr.tlength*3)/2; - s->buffer_attr.minreq = s->buffer_attr.tlength/100; - s->buffer_attr.prebuf = s->buffer_attr.tlength - s->buffer_attr.minreq; - s->buffer_attr.fragsize = s->buffer_attr.tlength/100; - } - - pa_stream_set_state(s, PA_STREAM_CREATING); - - t = pa_tagstruct_new(NULL, 0); - assert(t); - - if (!dev) { - if (s->direction == PA_STREAM_PLAYBACK) - dev = s->context->conf->default_sink; - else - dev = s->context->conf->default_source; - } - - pa_tagstruct_put(t, - PA_TAG_U32, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM, - PA_TAG_U32, tag = s->context->ctag++, - PA_TAG_STRING, s->name, - PA_TAG_SAMPLE_SPEC, &s->sample_spec, - PA_TAG_CHANNEL_MAP, &s->channel_map, - PA_TAG_U32, PA_INVALID_INDEX, - PA_TAG_STRING, dev, - PA_TAG_U32, s->buffer_attr.maxlength, - PA_TAG_BOOLEAN, !!(flags & PA_STREAM_START_CORKED), - PA_TAG_INVALID); - - if (s->direction == PA_STREAM_PLAYBACK) { - pa_cvolume cv; - pa_tagstruct_put(t, - PA_TAG_U32, s->buffer_attr.tlength, - PA_TAG_U32, s->buffer_attr.prebuf, - PA_TAG_U32, s->buffer_attr.minreq, - PA_TAG_INVALID); - - if (!volume) { - pa_cvolume_reset(&cv, s->sample_spec.channels); - volume = &cv; - } - - pa_tagstruct_put_cvolume(t, volume); - } else - pa_tagstruct_putu32(t, s->buffer_attr.fragsize); - - pa_pstream_send_tagstruct(s->context->pstream, t); - pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s); - - pa_stream_unref(s); -} - -void pa_stream_connect_playback(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags, pa_cvolume *volume) { - assert(s && s->context->state == PA_CONTEXT_READY && s->ref >= 1); - s->direction = PA_STREAM_PLAYBACK; - create_stream(s, dev, attr, flags, volume); -} - -void pa_stream_connect_record(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags) { - assert(s && s->context->state == PA_CONTEXT_READY && s->ref >= 1); - s->direction = PA_STREAM_RECORD; - create_stream(s, dev, attr, flags, 0); -} - -void pa_stream_write(pa_stream *s, const void *data, size_t length, void (*free_cb)(void *p), size_t delta) { - pa_memchunk chunk; - assert(s && s->context && data && length && s->state == PA_STREAM_READY && s->ref >= 1); - - if (free_cb) { - chunk.memblock = pa_memblock_new_user((void*) data, length, free_cb, 1, s->context->memblock_stat); - assert(chunk.memblock && chunk.memblock->data); - } else { - chunk.memblock = pa_memblock_new(length, s->context->memblock_stat); - assert(chunk.memblock && chunk.memblock->data); - memcpy(chunk.memblock->data, data, length); - } - chunk.index = 0; - chunk.length = length; - - pa_pstream_send_memblock(s->context->pstream, s->channel, delta, &chunk); - pa_memblock_unref(chunk.memblock); - - if (length < s->requested_bytes) - s->requested_bytes -= length; - else - s->requested_bytes = 0; - - s->counter += length; -} - -size_t pa_stream_writable_size(pa_stream *s) { - assert(s && s->ref >= 1); - return s->state == PA_STREAM_READY ? s->requested_bytes : 0; -} - -pa_operation * pa_stream_drain(pa_stream *s, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(s && s->ref >= 1 && s->state == PA_STREAM_READY); - - o = pa_operation_new(s->context, s); - assert(o); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_DRAIN_PLAYBACK_STREAM); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - pa_tagstruct_putu32(t, s->channel); - pa_pstream_send_tagstruct(s->context->pstream, t); - pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o); - - return pa_operation_ref(o); -} - -static void stream_get_latency_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - pa_latency_info i, *p = NULL; - struct timeval local, remote, now; - assert(pd && o && o->stream && o->context); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - } else if (pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 || - pa_tagstruct_get_usec(t, &i.sink_usec) < 0 || - pa_tagstruct_get_usec(t, &i.source_usec) < 0 || - pa_tagstruct_get_boolean(t, &i.playing) < 0 || - pa_tagstruct_getu32(t, &i.queue_length) < 0 || - pa_tagstruct_get_timeval(t, &local) < 0 || - pa_tagstruct_get_timeval(t, &remote) < 0 || - pa_tagstruct_getu64(t, &i.counter) < 0 || - !pa_tagstruct_eof(t)) { - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } else { - pa_gettimeofday(&now); - - if (pa_timeval_cmp(&local, &remote) <= 0 && pa_timeval_cmp(&remote, &now) <= 0) { - /* local and remote seem to have synchronized clocks */ - - if (o->stream->direction == PA_STREAM_PLAYBACK) - i.transport_usec = pa_timeval_diff(&remote, &local); - else - i.transport_usec = pa_timeval_diff(&now, &remote); - - i.synchronized_clocks = 1; - i.timestamp = remote; - } else { - /* clocks are not synchronized, let's estimate latency then */ - i.transport_usec = pa_timeval_diff(&now, &local)/2; - i.synchronized_clocks = 0; - i.timestamp = local; - pa_timeval_add(&i.timestamp, i.transport_usec); - } - - if (o->stream->interpolate) { -/* pa_log("new interpol data\n"); */ - o->stream->ipol_timestamp = i.timestamp; - o->stream->ipol_usec = pa_stream_get_time(o->stream, &i); - o->stream->ipol_requested = 0; - } - - p = &i; - } - - if (o->callback) { - void (*cb)(pa_stream *s, const pa_latency_info *_i, void *_userdata) = (void (*)(pa_stream *s, const pa_latency_info *_i, void *_userdata)) o->callback; - cb(o->stream, p, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_stream_get_latency_info(pa_stream *s, void (*cb)(pa_stream *p, const pa_latency_info*i, void *userdata), void *userdata) { - uint32_t tag; - pa_operation *o; - pa_tagstruct *t; - struct timeval now; - assert(s && s->direction != PA_STREAM_UPLOAD); - - o = pa_operation_new(s->context, s); - assert(o); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - pa_tagstruct_putu32(t, s->channel); - - pa_gettimeofday(&now); - pa_tagstruct_put_timeval(t, &now); - pa_tagstruct_putu64(t, s->counter); - - pa_pstream_send_tagstruct(s->context->pstream, t); - pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_info_callback, o); - - return pa_operation_ref(o); -} - -void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_stream *s = userdata; - assert(pd && s && s->ref >= 1); - - pa_stream_ref(s); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(s->context, command, t) < 0) - goto finish; - - pa_stream_set_state(s, PA_STREAM_FAILED); - goto finish; - } else if (!pa_tagstruct_eof(t)) { - pa_context_fail(s->context, PA_ERROR_PROTOCOL); - goto finish; - } - - pa_stream_set_state(s, PA_STREAM_TERMINATED); - -finish: - pa_stream_unref(s); -} - -void pa_stream_disconnect(pa_stream *s) { - pa_tagstruct *t; - uint32_t tag; - assert(s && s->ref >= 1); - - if (!s->channel_valid || !s->context->state == PA_CONTEXT_READY) - return; - - pa_stream_ref(s); - - t = pa_tagstruct_new(NULL, 0); - assert(t); - - pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_DELETE_PLAYBACK_STREAM : - (s->direction == PA_STREAM_RECORD ? PA_COMMAND_DELETE_RECORD_STREAM : PA_COMMAND_DELETE_UPLOAD_STREAM)); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - pa_tagstruct_putu32(t, s->channel); - pa_pstream_send_tagstruct(s->context->pstream, t); - pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s); - - pa_stream_unref(s); -} - -void pa_stream_set_read_callback(pa_stream *s, void (*cb)(pa_stream *p, const void*data, size_t length, void *userdata), void *userdata) { - assert(s && s->ref >= 1); - s->read_callback = cb; - s->read_userdata = userdata; -} - -void pa_stream_set_write_callback(pa_stream *s, void (*cb)(pa_stream *p, size_t length, void *userdata), void *userdata) { - assert(s && s->ref >= 1); - s->write_callback = cb; - s->write_userdata = userdata; -} - -void pa_stream_set_state_callback(pa_stream *s, void (*cb)(pa_stream *s, void *userdata), void *userdata) { - assert(s && s->ref >= 1); - s->state_callback = cb; - s->state_userdata = userdata; -} - -void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_operation *o = userdata; - int success = 1; - assert(pd && o && o->context && o->ref >= 1); - - if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) - goto finish; - - success = 0; - } else if (!pa_tagstruct_eof(t)) { - pa_context_fail(o->context, PA_ERROR_PROTOCOL); - goto finish; - } - - if (o->callback) { - void (*cb)(pa_stream *s, int _success, void *_userdata) = (void (*)(pa_stream *s, int _success, void *_userdata)) o->callback; - cb(o->stream, success, o->userdata); - } - -finish: - pa_operation_done(o); - pa_operation_unref(o); -} - -pa_operation* pa_stream_cork(pa_stream *s, int b, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(s && s->ref >= 1 && s->state == PA_STREAM_READY); - - if (s->interpolate) { - if (!s->corked && b) - /* Pausing */ - s->ipol_usec = pa_stream_get_interpolated_time(s); - else if (s->corked && !b) - /* Unpausing */ - pa_gettimeofday(&s->ipol_timestamp); - } - - s->corked = b; - - o = pa_operation_new(s->context, s); - assert(o); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CORK_PLAYBACK_STREAM : PA_COMMAND_CORK_RECORD_STREAM); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - pa_tagstruct_putu32(t, s->channel); - pa_tagstruct_put_boolean(t, !!b); - pa_pstream_send_tagstruct(s->context->pstream, t); - pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o); - - pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); - - return pa_operation_ref(o); -} - -static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - assert(s && s->ref >= 1 && s->state == PA_STREAM_READY); - - o = pa_operation_new(s->context, s); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, command); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - pa_tagstruct_putu32(t, s->channel); - pa_pstream_send_tagstruct(s->context->pstream, t); - pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o); - - return pa_operation_ref(o); -} - -pa_operation* pa_stream_flush(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) { - pa_operation *o; - o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata); - pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); - return o; -} - -pa_operation* pa_stream_prebuf(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) { - pa_operation *o; - o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata); - pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); - return o; -} - -pa_operation* pa_stream_trigger(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) { - pa_operation *o; - o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata); - pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); - return o; -} - -pa_operation* pa_stream_set_name(pa_stream *s, const char *name, void(*cb)(pa_stream*c, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(s && s->ref >= 1 && s->state == PA_STREAM_READY && name && s->direction != PA_STREAM_UPLOAD); - - o = pa_operation_new(s->context, s); - assert(o); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - pa_tagstruct_putu32(t, s->channel); - pa_tagstruct_puts(t, name); - pa_pstream_send_tagstruct(s->context->pstream, t); - pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o); - - return pa_operation_ref(o); -} - -uint64_t pa_stream_get_counter(pa_stream *s) { - assert(s); - return s->counter; -} - -pa_usec_t pa_stream_get_time(pa_stream *s, const pa_latency_info *i) { - pa_usec_t usec; - assert(s); - - usec = pa_bytes_to_usec(i->counter, &s->sample_spec); - - if (i) { - if (s->direction == PA_STREAM_PLAYBACK) { - pa_usec_t latency = i->transport_usec + i->buffer_usec + i->sink_usec; - if (usec < latency) - usec = 0; - else - usec -= latency; - - } else if (s->direction == PA_STREAM_RECORD) { - usec += i->source_usec + i->buffer_usec + i->transport_usec; - - if (usec > i->sink_usec) - usec -= i->sink_usec; - else - usec = 0; - } - } - - if (usec < s->previous_time) - usec = s->previous_time; - - s->previous_time = usec; - - return usec; -} - -static pa_usec_t time_counter_diff(pa_stream *s, pa_usec_t t, pa_usec_t c, int *negative) { - assert(s); - - if (negative) - *negative = 0; - - if (c < t) { - if (s->direction == PA_STREAM_RECORD) { - if (negative) - *negative = 1; - - return t-c; - } else - return 0; - } else - return c-t; -} - -pa_usec_t pa_stream_get_latency(pa_stream *s, const pa_latency_info *i, int *negative) { - pa_usec_t t, c; - assert(s && i); - - t = pa_stream_get_time(s, i); - c = pa_bytes_to_usec(s->counter, &s->sample_spec); - - return time_counter_diff(s, t, c, negative); -} - -const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) { - assert(s); - return &s->sample_spec; -} - -void pa_stream_trash_ipol(pa_stream *s) { - assert(s); - - if (!s->interpolate) - return; - - memset(&s->ipol_timestamp, 0, sizeof(s->ipol_timestamp)); - s->ipol_usec = 0; -} - -pa_usec_t pa_stream_get_interpolated_time(pa_stream *s) { - pa_usec_t usec; - assert(s && s->interpolate); - - if (s->corked) - usec = s->ipol_usec; - else { - if (s->ipol_timestamp.tv_sec == 0) - usec = 0; - else - usec = s->ipol_usec + pa_timeval_age(&s->ipol_timestamp); - } - - if (usec < s->previous_ipol_time) - usec = s->previous_ipol_time; - - s->previous_ipol_time = usec; - - return usec; -} - -pa_usec_t pa_stream_get_interpolated_latency(pa_stream *s, int *negative) { - pa_usec_t t, c; - assert(s && s->interpolate); - - t = pa_stream_get_interpolated_time(s); - c = pa_bytes_to_usec(s->counter, &s->sample_spec); - return time_counter_diff(s, t, c, negative); -} diff --git a/polyp/polyplib-stream.h b/polyp/polyplib-stream.h deleted file mode 100644 index bc828b71..00000000 --- a/polyp/polyplib-stream.h +++ /dev/null @@ -1,181 +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 - -#include -#include -#include -#include -#include -#include - -/** \file - * Audio streams for input, output and sample upload */ - -PA_C_DECL_BEGIN - -/** \pa_stream - * An opaque stream for playback or recording */ -typedef struct pa_stream pa_stream; - -/** Create a new, unconnected stream with the specified name and sample type */ -pa_stream* pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map); - -/** Decrease the reference counter by one */ -void pa_stream_unref(pa_stream *s); - -/** Increase the reference counter by one */ -pa_stream *pa_stream_ref(pa_stream *s); - -/** Return the current state of the stream */ -pa_stream_state_t pa_stream_get_state(pa_stream *p); - -/** Return the context this stream is attached to */ -pa_context* pa_stream_get_context(pa_stream *p); - -/** Return the device (sink input or source output) index this stream is connected to */ -uint32_t pa_stream_get_index(pa_stream *s); - -/** Connect the stream to a sink */ -void pa_stream_connect_playback( - pa_stream *s, - const char *dev, - const pa_buffer_attr *attr, - pa_stream_flags_t flags, - pa_cvolume *volume); - -/** Connect the stream to a source */ -void pa_stream_connect_record( - pa_stream *s, - const char *dev, - const pa_buffer_attr *attr, - pa_stream_flags_t flags); - -/** Disconnect a stream from a source/sink */ -void pa_stream_disconnect(pa_stream *s); - -/** Write some data to the server (for playback sinks), if free_cb is - * non-NULL this routine is called when all data has been written out - * and an internal reference to the specified data is kept, the data - * is not copied. If NULL, the data is copied into an internal - * buffer. */ -void pa_stream_write(pa_stream *p /**< The stream to use */, - const void *data /**< The data to write */, - size_t length /**< The length of the data to write */, - void (*free_cb)(void *p) /**< A cleanup routine for the data or NULL to request an internal copy */, - size_t delta /**< Drop this many - bytes in the playback - buffer before writing - this data. Use - (size_t) -1 for - clearing the whole - playback - buffer. Normally you - will specify 0 here, - i.e. append to the - playback buffer. If - the value given here - is greater than the - buffered data length - the buffer is cleared - and the data is - written to the - buffer's start. This - value is ignored on - upload streams. */); - -/** Return the amount of bytes that may be written using pa_stream_write() */ -size_t pa_stream_writable_size(pa_stream *p); - -/** Drain a playback stream */ -pa_operation* pa_stream_drain(pa_stream *s, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata); - -/** Get the playback latency of a stream */ -pa_operation* pa_stream_get_latency_info(pa_stream *p, void (*cb)(pa_stream *p, const pa_latency_info *i, void *userdata), void *userdata); - -/** Set the callback function that is called whenever the state of the stream changes */ -void pa_stream_set_state_callback(pa_stream *s, void (*cb)(pa_stream *s, void *userdata), void *userdata); - -/** Set the callback function that is called when new data may be - * written to the stream. */ -void pa_stream_set_write_callback(pa_stream *p, void (*cb)(pa_stream *p, size_t length, void *userdata), void *userdata); - -/** Set the callback function that is called when new data is available from the stream */ -void pa_stream_set_read_callback(pa_stream *p, void (*cb)(pa_stream *p, const void*data, size_t length, void *userdata), void *userdata); - -/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. \since 0.3 */ -pa_operation* pa_stream_cork(pa_stream *s, int b, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata); - -/** Flush the playback buffer of this stream. Most of the time you're - * better off using the parameter delta of pa_stream_write() instead of this - * function. Available on both playback and recording streams. \since 0.3 */ -pa_operation* pa_stream_flush(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata); - -/** Reenable prebuffering. Available for playback streams only. \since 0.6 */ -pa_operation* pa_stream_prebuf(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata); - -/** Request immediate start of playback on this stream. This disables - * prebuffering as specified in the pa_buffer_attr structure. Available for playback streams only. \since - * 0.3 */ -pa_operation* pa_stream_trigger(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata); - -/** Rename the stream. \since 0.5 */ -pa_operation* pa_stream_set_name(pa_stream *s, const char *name, void(*cb)(pa_stream*c, int success, void *userdata), void *userdata); - -/** Return the total number of bytes written to/read from the - * stream. This counter is not reset on pa_stream_flush(), you may do - * this yourself using pa_stream_reset_counter(). \since 0.6 */ -uint64_t pa_stream_get_counter(pa_stream *s); - -/** Return the current playback/recording time. This is based on the - * counter accessible with pa_stream_get_counter(). This function - * requires a pa_latency_info structure as argument, which should be - * acquired using pa_stream_get_latency(). \since 0.6 */ -pa_usec_t pa_stream_get_time(pa_stream *s, const pa_latency_info *i); - -/** Return the total stream latency. Thus function requires a - * pa_latency_info structure as argument, which should be aquired - * using pa_stream_get_latency(). In case the stream is a monitoring - * stream the result can be negative, i.e. the captured samples are - * not yet played. In this case *negative is set to 1. \since 0.6 */ -pa_usec_t pa_stream_get_latency(pa_stream *s, const pa_latency_info *i, int *negative); - -/** Return the interpolated playback/recording time. Requires the - * PA_STREAM_INTERPOLATE_LATENCY bit set when creating the stream. In - * contrast to pa_stream_get_latency() this function doesn't require - * a whole roundtrip for response. \since 0.6 */ -pa_usec_t pa_stream_get_interpolated_time(pa_stream *s); - -/** Return the interpolated playback/recording latency. Requires the - * PA_STREAM_INTERPOLATE_LATENCY bit set when creating the - * stream. \since 0.6 */ -pa_usec_t pa_stream_get_interpolated_latency(pa_stream *s, int *negative); - -/** Return a pointer to the streams sample specification. \since 0.6 */ -const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s); - -PA_C_DECL_END - -#endif diff --git a/polyp/polyplib-subscribe.c b/polyp/polyplib-subscribe.c deleted file mode 100644 index ef90ab0b..00000000 --- a/polyp/polyplib-subscribe.c +++ /dev/null @@ -1,81 +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 -#endif - -#include -#include - -#include "polyplib-subscribe.h" -#include "polyplib-internal.h" -#include "pstream-util.h" -#include "gccmacro.h" - -void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { - pa_context *c = userdata; - pa_subscription_event_type_t e; - uint32_t index; - assert(pd && command == PA_COMMAND_SUBSCRIBE_EVENT && t && c); - - pa_context_ref(c); - - if (pa_tagstruct_getu32(t, &e) < 0 || - pa_tagstruct_getu32(t, &index) < 0 || - !pa_tagstruct_eof(t)) { - pa_context_fail(c, PA_ERROR_PROTOCOL); - goto finish; - } - - if (c->subscribe_callback) - c->subscribe_callback(c, e, index, c->subscribe_userdata); - -finish: - pa_context_unref(c); -} - - -pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { - pa_operation *o; - pa_tagstruct *t; - uint32_t tag; - assert(c); - - o = pa_operation_new(c, NULL); - o->callback = (pa_operation_callback) cb; - o->userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_putu32(t, m); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); - - return pa_operation_ref(o); -} - -void pa_context_set_subscribe_callback(pa_context *c, void (*cb)(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata) { - assert(c); - c->subscribe_callback = cb; - c->subscribe_userdata = userdata; -} diff --git a/polyp/polyplib-subscribe.h b/polyp/polyplib-subscribe.h deleted file mode 100644 index 920c9853..00000000 --- a/polyp/polyplib-subscribe.h +++ /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 - -#include -#include -#include - -/** \file - * Daemon introspection event subscription subsystem. Use this - * to be notified whenever the internal layout of daemon changes: - * i.e. entities such as sinks or sources are create, removed or - * modified. */ - -PA_C_DECL_BEGIN - -/** Enable event notification */ -pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); - -/** Set the context specific call back function that is called whenever the state of the daemon changes */ -void pa_context_set_subscribe_callback(pa_context *c, void (*cb)(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata); - -PA_C_DECL_END - -#endif diff --git a/polyp/polyplib-version.h.in b/polyp/polyplib-version.h.in deleted file mode 100644 index 89e0a0e5..00000000 --- a/polyp/polyplib-version.h.in +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef foopolyplibversionhfoo /*-*-C-*-*/ -#define foopolyplibversionhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -/* WARNING: Make sure to edit the real source file polyplib-version.h.in! */ - -/** \file - * Define header version */ - -PA_C_DECL_BEGIN - -/** Return the version of the header files. Keep in mind that this is -a macro and not a function, so it is impossible to get the pointer of -it. */ -#define pa_get_headers_version() ("@PACKAGE_VERSION@") - -/** Return the version of the library the current application is linked to. */ -const char* pa_get_library_version(void); - -/** The current API version. Version 6 relates to polypaudio - * 0.6. Prior versions (i.e. Polypaudio 0.5.1 and older) have - * PA_API_VERSION undefined. */ -#define PA_API_VERSION @PA_API_VERSION@ - -PA_C_DECL_END - -#endif diff --git a/polyp/polyplib.h b/polyp/polyplib.h deleted file mode 100644 index b9b9b447..00000000 --- a/polyp/polyplib.h +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** \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/props.c b/polyp/props.c deleted file mode 100644 index df748c1e..00000000 --- a/polyp/props.c +++ /dev/null @@ -1,119 +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 - -#include "xmalloc.h" -#include "props.h" -#include "log.h" - -typedef struct pa_property { - char *name; /* Points to memory allocated by the property subsystem */ - void *data; /* Points to memory maintained by the caller */ -} pa_property; - -/* Allocate a new property object */ -static pa_property* property_new(const char *name, void *data) { - pa_property* p; - assert(name && data); - - p = pa_xmalloc(sizeof(pa_property)); - p->name = pa_xstrdup(name); - p->data = data; - - return p; -} - -/* Free a property object */ -static void property_free(pa_property *p) { - assert(p); - - pa_xfree(p->name); - pa_xfree(p); -} - -void* pa_property_get(pa_core *c, const char *name) { - pa_property *p; - assert(c && name && c->properties); - - if (!(p = pa_hashmap_get(c->properties, name))) - return NULL; - - return p->data; -} - -int pa_property_set(pa_core *c, const char *name, void *data) { - pa_property *p; - assert(c && name && data && c->properties); - - if (pa_hashmap_get(c->properties, name)) - return -1; - - p = property_new(name, data); - pa_hashmap_put(c->properties, p->name, p); - return 0; -} - -int pa_property_remove(pa_core *c, const char *name) { - pa_property *p; - assert(c && name && c->properties); - - if (!(p = pa_hashmap_remove(c->properties, name))) - return -1; - - property_free(p); - return 0; -} - -void pa_property_init(pa_core *c) { - assert(c); - - c->properties = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); -} - -void pa_property_cleanup(pa_core *c) { - assert(c); - - if (!c->properties) - return; - - assert(!pa_hashmap_size(c->properties)); - - pa_hashmap_free(c->properties, NULL, NULL); - c->properties = NULL; - -} - -void pa_property_dump(pa_core *c, pa_strbuf *s) { - void *state = NULL; - pa_property *p; - assert(c && s); - - while ((p = pa_hashmap_iterate(c->properties, &state, NULL))) - pa_strbuf_printf(s, "[%s] -> [%p]\n", p->name, p->data); -} - -int pa_property_replace(pa_core *c, const char *name, void *data) { - assert(c && name); - - pa_property_remove(c, name); - return pa_property_set(c, name, data); -} diff --git a/polyp/props.h b/polyp/props.h deleted file mode 100644 index 5abf8787..00000000 --- a/polyp/props.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef foopropshfoo -#define foopropshfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "core.h" -#include "strbuf.h" - -/* The property subsystem is to be used to share data between - * modules. Consider them to be kind of "global" variables for a - * core. Why not use the hashmap functions directly? The hashmap - * functions copy neither the key nor value, while this property - * system copies the key. Users of this system have to think about - * reference counting themselves. */ - -/* Return a pointer to the value of the specified property. */ -void* pa_property_get(pa_core *c, const char *name); - -/* Set the property 'name' to 'data'. This function fails in case a - * property by this name already exists. The property data is not - * copied or reference counted. This is the caller's job. */ -int pa_property_set(pa_core *c, const char *name, void *data); - -/* Remove the specified property. Return non-zero on failure */ -int pa_property_remove(pa_core *c, const char *name); - -/* A combination of pa_property_remove() and pa_property_set() */ -int pa_property_replace(pa_core *c, const char *name, void *data); - -/* Free all memory used by the property system */ -void pa_property_cleanup(pa_core *c); - -/* Initialize the properties subsystem */ -void pa_property_init(pa_core *c); - -/* Dump the current set of properties */ -void pa_property_dump(pa_core *c, pa_strbuf *s); - -#endif diff --git a/polyp/protocol-cli.c b/polyp/protocol-cli.c deleted file mode 100644 index d2d73550..00000000 --- a/polyp/protocol-cli.c +++ /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 -#endif - -#include -#include - -#include "protocol-cli.h" -#include "cli.h" -#include "xmalloc.h" -#include "log.h" - -/* Don't allow more than this many concurrent connections */ -#define MAX_CONNECTIONS 25 - -struct pa_protocol_cli { - pa_module *module; - pa_core *core; - pa_socket_server*server; - pa_idxset *connections; -}; - -static void cli_eof_cb(pa_cli*c, void*userdata) { - pa_protocol_cli *p = userdata; - assert(p); - pa_idxset_remove_by_data(p->connections, c, NULL); - pa_cli_free(c); -} - -static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) { - pa_protocol_cli *p = userdata; - pa_cli *c; - assert(s && io && p); - - if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) { - pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS); - pa_iochannel_free(io); - return; - } - - c = pa_cli_new(p->core, io, p->module); - assert(c); - pa_cli_set_eof_callback(c, cli_eof_cb, p); - - pa_idxset_put(p->connections, c, NULL); -} - -pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) { - pa_protocol_cli* p; - assert(core && server); - - p = pa_xmalloc(sizeof(pa_protocol_cli)); - p->module = m; - p->core = core; - p->server = server; - p->connections = pa_idxset_new(NULL, NULL); - - pa_socket_server_set_callback(p->server, on_connection, p); - - return p; -} - -static void free_connection(void *p, PA_GCC_UNUSED void *userdata) { - assert(p); - pa_cli_free(p); -} - -void pa_protocol_cli_free(pa_protocol_cli *p) { - assert(p); - - pa_idxset_free(p->connections, free_connection, NULL); - pa_socket_server_unref(p->server); - pa_xfree(p); -} diff --git a/polyp/protocol-cli.h b/polyp/protocol-cli.h deleted file mode 100644 index aed733c1..00000000 --- a/polyp/protocol-cli.h +++ /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" - -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/polyp/protocol-esound.c b/polyp/protocol-esound.c deleted file mode 100644 index 6f004e16..00000000 --- a/polyp/protocol-esound.c +++ /dev/null @@ -1,1166 +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 -#endif - -#include -#include -#include -#include -#include -#include - -#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" -#include "util.h" -#include "endianmacros.h" - -/* Don't accept more connection than this */ -#define MAX_CONNECTIONS 10 - -/* Kick a client if it doesn't authenticate within this time */ -#define AUTH_TIMEOUT 5 - -#define DEFAULT_COOKIE_FILE ".esd_auth" - -#define PLAYBACK_BUFFER_SECONDS (.5) -#define PLAYBACK_BUFFER_FRAGMENTS (10) -#define RECORD_BUFFER_SECONDS (5) -#define RECORD_BUFFER_FRAGMENTS (100) - -#define MAX_CACHE_SAMPLE_SIZE (1024000) - -#define SCACHE_PREFIX "esound." - -/* This is heavily based on esound's code */ - -struct connection { - uint32_t index; - int dead; - pa_protocol_esound *protocol; - pa_iochannel *io; - pa_client *client; - int authorized, swap_byte_order; - void *write_data; - size_t write_data_alloc, write_data_index, write_data_length; - void *read_data; - size_t read_data_alloc, read_data_length; - esd_proto_t request; - esd_client_state_t state; - pa_sink_input *sink_input; - pa_source_output *source_output; - pa_memblockq *input_memblockq, *output_memblockq; - pa_defer_event *defer_event; - - struct { - pa_memblock *current_memblock; - size_t memblock_index, fragment_size; - } playback; - - struct { - pa_memchunk memchunk; - char *name; - pa_sample_spec sample_spec; - } scache; - - pa_time_event *auth_timeout_event; -}; - -struct pa_protocol_esound { - int public; - pa_module *module; - pa_core *core; - pa_socket_server *server; - pa_idxset *connections; - char *sink_name, *source_name; - unsigned n_player; - uint8_t esd_key[ESD_KEY_LEN]; -}; - -typedef struct proto_handler { - size_t data_length; - int (*proc)(struct connection *c, esd_proto_t request, const void *data, size_t length); - const char *description; -} esd_proto_handler_info_t; - -static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length); -static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk); -static void sink_input_kill_cb(pa_sink_input *i); -static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i); -static pa_usec_t source_output_get_latency_cb(pa_source_output *o); - -static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk); -static void source_output_kill_cb(pa_source_output *o); - -static int esd_proto_connect(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_stream_play(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_get_latency(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_server_info(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_sample_cache(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_sample_get_id(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_standby_or_resume(struct connection *c, esd_proto_t request, const void *data, size_t length); - -/* the big map of protocol handler info */ -static struct proto_handler proto_map[ESD_PROTO_MAX] = { - { ESD_KEY_LEN + sizeof(int), esd_proto_connect, "connect" }, - { ESD_KEY_LEN + sizeof(int), NULL, "lock" }, - { ESD_KEY_LEN + sizeof(int), NULL, "unlock" }, - - { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_play, "stream play" }, - { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream rec" }, - { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream mon" }, - - { ESD_NAME_MAX + 3 * sizeof(int), esd_proto_sample_cache, "sample cache" }, /* 6 */ - { sizeof(int), esd_proto_sample_free_or_play, "sample free" }, - { sizeof(int), esd_proto_sample_free_or_play, "sample play" }, /* 8 */ - { sizeof(int), NULL, "sample loop" }, - { sizeof(int), NULL, "sample stop" }, - { -1, NULL, "TODO: sample kill" }, - - { ESD_KEY_LEN + sizeof(int), esd_proto_standby_or_resume, "standby" }, /* NOOP! */ - { ESD_KEY_LEN + sizeof(int), esd_proto_standby_or_resume, "resume" }, /* NOOP! */ /* 13 */ - - { ESD_NAME_MAX, esd_proto_sample_get_id, "sample getid" }, /* 14 */ - { ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" }, - - { sizeof(int), esd_proto_server_info, "server info" }, - { sizeof(int), esd_proto_all_info, "all info" }, - { -1, NULL, "TODO: subscribe" }, - { -1, NULL, "TODO: unsubscribe" }, - - { 3 * sizeof(int), esd_proto_stream_pan, "stream pan"}, - { 3 * sizeof(int), NULL, "sample pan" }, - - { sizeof(int), NULL, "standby mode" }, - { 0, esd_proto_get_latency, "get latency" } -}; - - -static void connection_free(struct connection *c) { - assert(c); - pa_idxset_remove_by_data(c->protocol->connections, c, NULL); - - if (c->state == ESD_STREAMING_DATA) - c->protocol->n_player--; - - pa_client_free(c->client); - - if (c->sink_input) { - pa_sink_input_disconnect(c->sink_input); - pa_sink_input_unref(c->sink_input); - } - - if (c->source_output) { - pa_source_output_disconnect(c->source_output); - pa_source_output_unref(c->source_output); - } - - if (c->input_memblockq) - pa_memblockq_free(c->input_memblockq); - if (c->output_memblockq) - pa_memblockq_free(c->output_memblockq); - - if (c->playback.current_memblock) - pa_memblock_unref(c->playback.current_memblock); - - pa_xfree(c->read_data); - pa_xfree(c->write_data); - - if (c->io) - pa_iochannel_free(c->io); - - if (c->defer_event) - c->protocol->core->mainloop->defer_free(c->defer_event); - - if (c->scache.memchunk.memblock) - pa_memblock_unref(c->scache.memchunk.memblock); - pa_xfree(c->scache.name); - - if (c->auth_timeout_event) - c->protocol->core->mainloop->time_free(c->auth_timeout_event); - - pa_xfree(c); -} - -static void* connection_write(struct connection *c, size_t length) { - size_t t, i; - assert(c); - - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); - c->protocol->core->mainloop->defer_enable(c->defer_event, 1); - - t = c->write_data_length+length; - - if (c->write_data_alloc < t) - c->write_data = pa_xrealloc(c->write_data, c->write_data_alloc = t); - - assert(c->write_data); - - i = c->write_data_length; - c->write_data_length += length; - - return (uint8_t*) c->write_data+i; -} - -static void format_esd2native(int format, int swap_bytes, pa_sample_spec *ss) { - assert(ss); - - ss->channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1; - if ((format & ESD_MASK_BITS) == ESD_BITS16) - ss->format = swap_bytes ? PA_SAMPLE_S16RE : PA_SAMPLE_S16NE; - else - ss->format = PA_SAMPLE_U8; -} - -static int format_native2esd(pa_sample_spec *ss) { - int format = 0; - - format = (ss->format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16; - format |= (ss->channels >= 2) ? ESD_STEREO : ESD_MONO; - - return format; -} - -/*** esound commands ***/ - -static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { - uint32_t ekey; - int *ok; - assert(length == (ESD_KEY_LEN + sizeof(uint32_t))); - - if (!c->authorized) { - if (memcmp(data, c->protocol->esd_key, ESD_KEY_LEN) != 0) { - pa_log(__FILE__": kicked client with invalid authorization key.\n"); - return -1; - } - - c->authorized = 1; - if (c->auth_timeout_event) { - c->protocol->core->mainloop->time_free(c->auth_timeout_event); - c->auth_timeout_event = NULL; - } - } - - ekey = *(const uint32_t*)((const uint8_t*) data+ESD_KEY_LEN); - if (ekey == ESD_ENDIAN_KEY) - c->swap_byte_order = 0; - else if (ekey == ESD_SWAP_ENDIAN_KEY) - c->swap_byte_order = 1; - else { - pa_log(__FILE__": client sent invalid endian key\n"); - return -1; - } - - ok = connection_write(c, sizeof(int)); - assert(ok); - *ok = 1; - return 0; -} - -static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { - char name[ESD_NAME_MAX]; - int format, rate; - pa_sink *sink; - pa_sample_spec ss; - size_t l; - assert(c && length == (sizeof(int)*2+ESD_NAME_MAX)); - - format = MAYBE_INT32_SWAP(c->swap_byte_order, *(const int*)data); - rate = MAYBE_INT32_SWAP(c->swap_byte_order, *((const int*)data + 1)); - - ss.rate = rate; - format_esd2native(format, c->swap_byte_order, &ss); - - if (!pa_sample_spec_valid(&ss)) { - pa_log(__FILE__": invalid sample specification\n"); - return -1; - } - - if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) { - pa_log(__FILE__": no such sink\n"); - return -1; - } - - strncpy(name, (const char*) data + sizeof(int)*2, sizeof(name)); - name[sizeof(name)-1] = 0; - - pa_client_set_name(c->client, name); - - assert(!c->sink_input && !c->input_memblockq); - - if (!(c->sink_input = pa_sink_input_new(sink, __FILE__, name, &ss, NULL, 0, -1))) { - pa_log(__FILE__": failed to create sink input.\n"); - return -1; - } - - l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS); - c->input_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&ss), l/2, l/PLAYBACK_BUFFER_FRAGMENTS, c->protocol->core->memblock_stat); - pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2); - c->playback.fragment_size = l/10; - - c->sink_input->owner = c->protocol->module; - c->sink_input->client = c->client; - c->sink_input->peek = sink_input_peek_cb; - c->sink_input->drop = sink_input_drop_cb; - c->sink_input->kill = sink_input_kill_cb; - c->sink_input->get_latency = sink_input_get_latency_cb; - c->sink_input->userdata = c; - - c->state = ESD_STREAMING_DATA; - - c->protocol->n_player++; - - return 0; -} - -static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length) { - char name[ESD_NAME_MAX]; - int format, rate; - pa_source *source; - pa_sample_spec ss; - size_t l; - assert(c && length == (sizeof(int)*2+ESD_NAME_MAX)); - - format = MAYBE_INT32_SWAP(c->swap_byte_order, *(const int*)data); - rate = MAYBE_INT32_SWAP(c->swap_byte_order, *((const int*)data + 1)); - - ss.rate = rate; - format_esd2native(format, c->swap_byte_order, &ss); - - if (!pa_sample_spec_valid(&ss)) { - pa_log(__FILE__": invalid sample specification.\n"); - return -1; - } - - if (request == ESD_PROTO_STREAM_MON) { - pa_sink* sink; - - if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) { - pa_log(__FILE__": no such sink.\n"); - return -1; - } - - if (!(source = sink->monitor_source)) { - pa_log(__FILE__": no such monitor source.\n"); - return -1; - } - } else { - assert(request == ESD_PROTO_STREAM_REC); - - if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, 1))) { - pa_log(__FILE__": no such source.\n"); - return -1; - } - } - - strncpy(name, (const char*) data + sizeof(int)*2, sizeof(name)); - name[sizeof(name)-1] = 0; - - pa_client_set_name(c->client, name); - - assert(!c->output_memblockq && !c->source_output); - - if (!(c->source_output = pa_source_output_new(source, __FILE__, name, &ss, NULL, -1))) { - pa_log(__FILE__": failed to create source output\n"); - return -1; - } - - l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS); - c->output_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&ss), 0, 0, c->protocol->core->memblock_stat); - pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2); - - c->source_output->owner = c->protocol->module; - c->source_output->client = c->client; - c->source_output->push = source_output_push_cb; - c->source_output->kill = source_output_kill_cb; - c->source_output->get_latency = source_output_get_latency_cb; - c->source_output->userdata = c; - - c->state = ESD_STREAMING_DATA; - - c->protocol->n_player++; - - return 0; -} - -static int esd_proto_get_latency(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { - pa_sink *sink; - int latency, *lag; - assert(c && !data && length == 0); - - if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) - latency = 0; - else { - double usec = pa_sink_get_latency(sink); - latency = (int) ((usec*44100)/1000000); - } - - lag = connection_write(c, sizeof(int)); - assert(lag); - *lag = MAYBE_INT32_SWAP(c->swap_byte_order, latency); - return 0; -} - -static int esd_proto_server_info(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { - int rate = 44100, format = ESD_STEREO|ESD_BITS16; - int *response; - pa_sink *sink; - assert(c && data && length == sizeof(int)); - - if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) { - rate = sink->sample_spec.rate; - format = format_native2esd(&sink->sample_spec); - } - - response = connection_write(c, sizeof(int)*3); - assert(response); - *(response++) = 0; - *(response++) = MAYBE_INT32_SWAP(c->swap_byte_order, rate); - *(response++) = MAYBE_INT32_SWAP(c->swap_byte_order, format); - return 0; -} - -static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length) { - uint8_t *response; - size_t t, k, s; - struct connection *conn; - size_t idx = PA_IDXSET_INVALID; - unsigned nsamples; - assert(c && data && length == sizeof(int)); - - if (esd_proto_server_info(c, request, data, length) < 0) - return -1; - - k = sizeof(int)*5+ESD_NAME_MAX; - s = sizeof(int)*6+ESD_NAME_MAX; - nsamples = c->protocol->core->scache ? pa_idxset_size(c->protocol->core->scache) : 0; - response = connection_write(c, (t = s*(nsamples+1) + k*(c->protocol->n_player+1))); - assert(k); - - for (conn = pa_idxset_first(c->protocol->connections, &idx); conn; conn = pa_idxset_next(c->protocol->connections, &idx)) { - int format = ESD_BITS16 | ESD_STEREO, rate = 44100, lvolume = ESD_VOLUME_BASE, rvolume = ESD_VOLUME_BASE; - - if (conn->state != ESD_STREAMING_DATA) - continue; - - assert(t >= s+k+k); - - if (conn->sink_input) { - rate = conn->sink_input->sample_spec.rate; - lvolume = (conn->sink_input->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM; - rvolume = (conn->sink_input->volume.values[1]*ESD_VOLUME_BASE)/PA_VOLUME_NORM; - format = format_native2esd(&conn->sink_input->sample_spec); - } - - /* id */ - *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (int) (conn->index+1)); - response += sizeof(int); - - /* name */ - assert(conn->client); - strncpy((char*) response, conn->client->name, ESD_NAME_MAX); - response += ESD_NAME_MAX; - - /* rate */ - *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, rate); - response += sizeof(int); - - /* left */ - *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, lvolume); - response += sizeof(int); - - /*right*/ - *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, rvolume); - response += sizeof(int); - - /*format*/ - *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, format); - response += sizeof(int); - - t-= k; - } - - assert(t == s*(nsamples+1)+k); - memset(response, 0, k); - response += k; - t -= k; - - if (nsamples) { - pa_scache_entry *ce; - - idx = PA_IDXSET_INVALID; - for (ce = pa_idxset_first(c->protocol->core->scache, &idx); ce; ce = pa_idxset_next(c->protocol->core->scache, &idx)) { - assert(t >= s*2); - - /* id */ - *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1)); - response += sizeof(int); - - /* name */ - if (strncmp(ce->name, SCACHE_PREFIX, sizeof(SCACHE_PREFIX)-1) == 0) - strncpy((char*) response, ce->name+sizeof(SCACHE_PREFIX)-1, ESD_NAME_MAX); - else - snprintf((char*) response, ESD_NAME_MAX, "native.%s", ce->name); - response += ESD_NAME_MAX; - - /* rate */ - *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, ce->sample_spec.rate); - response += sizeof(int); - - /* left */ - *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM); - response += sizeof(int); - - /*right*/ - *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM); - response += sizeof(int); - - /*format*/ - *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ce->sample_spec)); - response += sizeof(int); - - /*length*/ - *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (int) ce->memchunk.length); - response += sizeof(int); - - t -= s; - } - } - - assert(t == s); - memset(response, 0, s); - - return 0; -} - -static int esd_proto_stream_pan(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { - int *ok; - uint32_t idx; - pa_volume_t lvolume, rvolume; - struct connection *conn; - assert(c && data && length == sizeof(int)*3); - - idx = MAYBE_UINT32_SWAP(c->swap_byte_order, *(const int*)data)-1; - lvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, *((const int*)data + 1)); - lvolume = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE; - rvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, *((const int*)data + 2)); - rvolume = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE; - - ok = connection_write(c, sizeof(int)); - assert(ok); - - if ((conn = pa_idxset_get_by_index(c->protocol->connections, idx))) { - assert(conn->sink_input); - conn->sink_input->volume.values[0] = lvolume; - conn->sink_input->volume.values[1] = rvolume; - conn->sink_input->volume.channels = 2; - *ok = 1; - } else - *ok = 0; - - return 0; -} - -static int esd_proto_sample_cache(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { - pa_sample_spec ss; - int format, rate; - size_t sc_length; - uint32_t idx; - int *ok; - char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1]; - assert(c && data && length == (ESD_NAME_MAX+3*sizeof(int))); - - format = MAYBE_INT32_SWAP(c->swap_byte_order, *(const int*)data); - rate = MAYBE_INT32_SWAP(c->swap_byte_order, *((const int*)data + 1)); - - ss.rate = rate; - format_esd2native(format, c->swap_byte_order, &ss); - - sc_length = (size_t) MAYBE_INT32_SWAP(c->swap_byte_order, (*((const int*)data + 2))); - - if (sc_length >= MAX_CACHE_SAMPLE_SIZE) - return -1; - - strcpy(name, SCACHE_PREFIX); - strncpy(name+sizeof(SCACHE_PREFIX)-1, (const char*) data+3*sizeof(int), ESD_NAME_MAX); - name[sizeof(name)-1] = 0; - - assert(!c->scache.memchunk.memblock); - c->scache.memchunk.memblock = pa_memblock_new(sc_length, c->protocol->core->memblock_stat); - c->scache.memchunk.index = 0; - c->scache.memchunk.length = sc_length; - c->scache.sample_spec = ss; - assert(!c->scache.name); - c->scache.name = pa_xstrdup(name); - - c->state = ESD_CACHING_SAMPLE; - - pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, &idx); - - ok = connection_write(c, sizeof(int)); - assert(ok); - - *ok = idx+1; - - return 0; -} - -static int esd_proto_sample_get_id(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { - int *ok; - uint32_t idx; - char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1]; - assert(c && data && length == ESD_NAME_MAX); - - ok = connection_write(c, sizeof(int)); - assert(ok); - - *ok = -1; - - strcpy(name, SCACHE_PREFIX); - strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX); - name[sizeof(name)-1] = 0; - - if ((idx = pa_scache_get_id_by_name(c->protocol->core, name)) != PA_IDXSET_INVALID) - *ok = (int) idx +1; - - return 0; -} - -static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length) { - int *ok; - const char *name; - uint32_t idx; - assert(c && data && length == sizeof(int)); - - idx = (uint32_t) MAYBE_INT32_SWAP(c->swap_byte_order, *(const int*)data)-1; - - ok = connection_write(c, sizeof(int)); - assert(ok); - - *ok = 0; - - if ((name = pa_scache_get_name_by_id(c->protocol->core, idx))) { - if (request == ESD_PROTO_SAMPLE_PLAY) { - pa_sink *sink; - - if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) - if (pa_scache_play_item(c->protocol->core, name, sink, NULL) >= 0) - *ok = (int) idx+1; - } else { - assert(request == ESD_PROTO_SAMPLE_FREE); - - if (pa_scache_remove_item(c->protocol->core, name) >= 0) - *ok = (int) idx+1; - } - } - - return 0; -} - -static int esd_proto_standby_or_resume(struct connection *c, PA_GCC_UNUSED esd_proto_t request, PA_GCC_UNUSED const void *data, PA_GCC_UNUSED size_t length) { - int *ok; - ok = connection_write(c, sizeof(int)*2); - assert(ok); - ok[0] = 1; - ok[1] = 1; - return 0; -} - -/*** client callbacks ***/ - -static void client_kill_cb(pa_client *c) { - assert(c && c->userdata); - connection_free(c->userdata); -} - -/*** pa_iochannel callbacks ***/ - -static int do_read(struct connection *c) { - assert(c && c->io); - -/* pa_log("READ\n"); */ - - if (c->state == ESD_NEXT_REQUEST) { - ssize_t r; - assert(c->read_data_length < sizeof(c->request)); - - if ((r = pa_iochannel_read(c->io, ((uint8_t*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) { - if (r != 0) - pa_log_warn(__FILE__": read() failed: %s\n", strerror(errno)); - return -1; - } - - if ((c->read_data_length+= r) >= sizeof(c->request)) { - struct proto_handler *handler; - - c->request = MAYBE_INT32_SWAP(c->swap_byte_order, c->request); - - if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) { - pa_log(__FILE__": recieved invalid request.\n"); - return -1; - } - - handler = proto_map+c->request; - -/* pa_log(__FILE__": executing request #%u\n", c->request); */ - - if (!handler->proc) { - pa_log(__FILE__": recieved unimplemented request #%u.\n", c->request); - return -1; - } - - if (handler->data_length == 0) { - c->read_data_length = 0; - - if (handler->proc(c, c->request, NULL, 0) < 0) - return -1; - - } else { - if (c->read_data_alloc < handler->data_length) - c->read_data = pa_xrealloc(c->read_data, c->read_data_alloc = handler->data_length); - assert(c->read_data); - - c->state = ESD_NEEDS_REQDATA; - c->read_data_length = 0; - } - } - - } else if (c->state == ESD_NEEDS_REQDATA) { - ssize_t r; - struct proto_handler *handler = proto_map+c->request; - - assert(handler->proc); - - assert(c->read_data && c->read_data_length < handler->data_length); - - if ((r = pa_iochannel_read(c->io, (uint8_t*) c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) { - if (r != 0) - pa_log_warn(__FILE__": read() failed: %s\n", strerror(errno)); - return -1; - } - - if ((c->read_data_length+= r) >= handler->data_length) { - size_t l = c->read_data_length; - assert(handler->proc); - - c->state = ESD_NEXT_REQUEST; - c->read_data_length = 0; - - if (handler->proc(c, c->request, c->read_data, l) < 0) - return -1; - } - } else if (c->state == ESD_CACHING_SAMPLE) { - ssize_t r; - - assert(c->scache.memchunk.memblock && c->scache.name && c->scache.memchunk.index < c->scache.memchunk.length); - - if ((r = pa_iochannel_read(c->io, (uint8_t*) c->scache.memchunk.memblock->data+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index)) <= 0) { - if (r!= 0) - pa_log_warn(__FILE__": read() failed: %s\n", strerror(errno)); - return -1; - } - - c->scache.memchunk.index += r; - assert(c->scache.memchunk.index <= c->scache.memchunk.length); - - if (c->scache.memchunk.index == c->scache.memchunk.length) { - uint32_t idx; - int *ok; - - c->scache.memchunk.index = 0; - pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, &idx); - - pa_memblock_unref(c->scache.memchunk.memblock); - c->scache.memchunk.memblock = NULL; - c->scache.memchunk.index = c->scache.memchunk.length = 0; - - pa_xfree(c->scache.name); - c->scache.name = NULL; - - c->state = ESD_NEXT_REQUEST; - - ok = connection_write(c, sizeof(int)); - assert(ok); - *ok = idx+1; - } - - } else if (c->state == ESD_STREAMING_DATA && c->sink_input) { - pa_memchunk chunk; - ssize_t r; - size_t l; - - assert(c->input_memblockq); - -/* pa_log("STREAMING_DATA\n"); */ - - if (!(l = pa_memblockq_missing(c->input_memblockq))) - return 0; - - if (l > c->playback.fragment_size) - l = c->playback.fragment_size; - - if (c->playback.current_memblock) - if (c->playback.current_memblock->length - c->playback.memblock_index < l) { - pa_memblock_unref(c->playback.current_memblock); - c->playback.current_memblock = NULL; - c->playback.memblock_index = 0; - } - - if (!c->playback.current_memblock) { - c->playback.current_memblock = pa_memblock_new(c->playback.fragment_size*2, c->protocol->core->memblock_stat); - assert(c->playback.current_memblock && c->playback.current_memblock->length >= l); - c->playback.memblock_index = 0; - } - - if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) { - if (r != 0) - pa_log(__FILE__": read() failed: %s\n", strerror(errno)); - return -1; - } - -/* pa_log(__FILE__": read %u\n", r); */ - - chunk.memblock = c->playback.current_memblock; - chunk.index = c->playback.memblock_index; - chunk.length = r; - assert(chunk.memblock); - - c->playback.memblock_index += r; - - assert(c->input_memblockq); - pa_memblockq_push_align(c->input_memblockq, &chunk, 0); - assert(c->sink_input); - pa_sink_notify(c->sink_input->sink); - } - - return 0; -} - -static int do_write(struct connection *c) { - assert(c && c->io); - -/* pa_log("WRITE\n"); */ - - if (c->write_data_length) { - ssize_t r; - - assert(c->write_data_index < c->write_data_length); - if ((r = pa_iochannel_write(c->io, (uint8_t*) c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) { - pa_log(__FILE__": write() failed: %s\n", strerror(errno)); - return -1; - } - - if ((c->write_data_index +=r) >= c->write_data_length) - c->write_data_length = c->write_data_index = 0; - - } else if (c->state == ESD_STREAMING_DATA && c->source_output) { - pa_memchunk chunk; - ssize_t r; - - assert(c->output_memblockq); - if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) - return 0; - - assert(chunk.memblock && chunk.length); - - if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) { - pa_memblock_unref(chunk.memblock); - pa_log(__FILE__": write(): %s\n", strerror(errno)); - return -1; - } - - pa_memblockq_drop(c->output_memblockq, &chunk, r); - pa_memblock_unref(chunk.memblock); - } - - return 0; -} - -static void do_work(struct connection *c) { - assert(c); - - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); - c->protocol->core->mainloop->defer_enable(c->defer_event, 0); - -/* pa_log("DOWORK %i\n", pa_iochannel_is_hungup(c->io)); */ - - if (!c->dead && pa_iochannel_is_readable(c->io)) - if (do_read(c) < 0) - goto fail; - - if (!c->dead && pa_iochannel_is_writable(c->io)) - if (do_write(c) < 0) - goto fail; - - /* In case the line was hungup, make sure to rerun this function - as soon as possible, until all data has been read. */ - - if (!c->dead && pa_iochannel_is_hungup(c->io)) - c->protocol->core->mainloop->defer_enable(c->defer_event, 1); - - return; - -fail: - - if (c->state == ESD_STREAMING_DATA && c->sink_input) { - c->dead = 1; - pa_memblockq_prebuf_disable(c->input_memblockq); - - pa_iochannel_free(c->io); - c->io = NULL; - - } else - connection_free(c); -} - -static void io_callback(pa_iochannel*io, void *userdata) { - struct connection *c = userdata; - assert(io && c && c->io == io); - -/* pa_log("IO\n"); */ - - do_work(c); -} - -/*** defer callback ***/ - -static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) { - struct connection *c = userdata; - assert(a && c && c->defer_event == e); - -/* pa_log("DEFER\n"); */ - - do_work(c); -} - -/*** sink_input callbacks ***/ - -static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) { - struct connection*c; - assert(i && i->userdata && chunk); - c = i->userdata; - - if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) { - - if (c->dead) - connection_free(c); - - return -1; - } - - return 0; -} - -static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { - struct connection*c = i->userdata; - assert(i && c && length); - -/* pa_log("DROP\n"); */ - - pa_memblockq_drop(c->input_memblockq, chunk, length); - - /* do something */ - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); - - if (!c->dead) - c->protocol->core->mainloop->defer_enable(c->defer_event, 1); - -/* assert(pa_memblockq_get_length(c->input_memblockq) > 2048); */ -} - -static void sink_input_kill_cb(pa_sink_input *i) { - assert(i && i->userdata); - connection_free((struct connection *) i->userdata); -} - -static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) { - struct connection*c = i->userdata; - assert(i && c); - return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); -} - -/*** source_output callbacks ***/ - -static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { - struct connection *c = o->userdata; - assert(o && c && chunk); - - pa_memblockq_push(c->output_memblockq, chunk, 0); - - /* do something */ - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); - - if (!c->dead) - c->protocol->core->mainloop->defer_enable(c->defer_event, 1); -} - -static void source_output_kill_cb(pa_source_output *o) { - assert(o && o->userdata); - connection_free((struct connection *) o->userdata); -} - -static pa_usec_t source_output_get_latency_cb(pa_source_output *o) { - struct connection*c = o->userdata; - assert(o && c); - return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec); -} - -/*** socket server callback ***/ - -static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) { - struct connection *c = userdata; - assert(m && tv && c && c->auth_timeout_event == e); - - if (!c->authorized) - connection_free(c); -} - -static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) { - struct connection *c; - pa_protocol_esound *p = userdata; - char cname[256]; - assert(s && io && p); - - if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) { - pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS); - pa_iochannel_free(io); - return; - } - - c = pa_xmalloc(sizeof(struct connection)); - c->protocol = p; - c->io = io; - pa_iochannel_set_callback(c->io, io_callback, c); - - pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); - assert(p->core); - c->client = pa_client_new(p->core, __FILE__, cname); - assert(c->client); - c->client->owner = p->module; - c->client->kill = client_kill_cb; - c->client->userdata = c; - - c->authorized = p->public; - c->swap_byte_order = 0; - c->dead = 0; - - c->read_data_length = 0; - c->read_data = pa_xmalloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length); - - c->write_data_length = c->write_data_index = c->write_data_alloc = 0; - c->write_data = NULL; - - c->state = ESD_NEEDS_REQDATA; - c->request = ESD_PROTO_CONNECT; - - c->sink_input = NULL; - c->input_memblockq = NULL; - - c->source_output = NULL; - c->output_memblockq = NULL; - - c->playback.current_memblock = NULL; - c->playback.memblock_index = 0; - c->playback.fragment_size = 0; - - c->scache.memchunk.length = c->scache.memchunk.index = 0; - c->scache.memchunk.memblock = NULL; - c->scache.name = NULL; - - if (!c->authorized) { - struct timeval tv; - pa_gettimeofday(&tv); - tv.tv_sec += AUTH_TIMEOUT; - c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c); - } else - c->auth_timeout_event = NULL; - - c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c); - assert(c->defer_event); - p->core->mainloop->defer_enable(c->defer_event, 0); - - pa_idxset_put(p->connections, c, &c->index); -} - -/*** entry points ***/ - -pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma) { - pa_protocol_esound *p; - int public = 0; - assert(core && server && ma); - - p = pa_xmalloc(sizeof(pa_protocol_esound)); - - if (pa_modargs_get_value_boolean(ma, "public", &public) < 0) { - pa_log(__FILE__": public= expects a boolean argument.\n"); - return NULL; - } - - if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", DEFAULT_COOKIE_FILE), p->esd_key, sizeof(p->esd_key)) < 0) { - pa_xfree(p); - return NULL; - } - - p->module = m; - p->public = public; - p->server = server; - pa_socket_server_set_callback(p->server, on_connection, p); - p->core = core; - p->connections = pa_idxset_new(NULL, NULL); - assert(p->connections); - - p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); - p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL)); - p->n_player = 0; - - return p; -} - -void pa_protocol_esound_free(pa_protocol_esound *p) { - struct connection *c; - assert(p); - - while ((c = pa_idxset_first(p->connections, NULL))) - connection_free(c); - - pa_idxset_free(p->connections, NULL, NULL); - pa_socket_server_unref(p->server); - pa_xfree(p); -} diff --git a/polyp/protocol-esound.h b/polyp/protocol-esound.h deleted file mode 100644 index 71d58464..00000000 --- a/polyp/protocol-esound.h +++ /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" - -typedef struct pa_protocol_esound pa_protocol_esound; - -pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma); -void pa_protocol_esound_free(pa_protocol_esound *p); - -#endif diff --git a/polyp/protocol-http.c b/polyp/protocol-http.c deleted file mode 100644 index 3e55df03..00000000 --- a/polyp/protocol-http.c +++ /dev/null @@ -1,265 +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 -#endif - -#include -#include -#include -#include - -#include "protocol-http.h" -#include "ioline.h" -#include "xmalloc.h" -#include "log.h" -#include "namereg.h" -#include "cli-text.h" - -/* Don't allow more than this many concurrent connections */ -#define MAX_CONNECTIONS 10 - -#define internal_server_error(c) http_message((c), 500, "Internal Server Error", NULL) - -#define URL_ROOT "/" -#define URL_CSS "/style" -#define URL_STATUS "/status" - -struct connection { - pa_protocol_http *protocol; - pa_ioline *line; - enum { REQUEST_LINE, MIME_HEADER, DATA } state; - char *url; -}; - -struct pa_protocol_http { - pa_module *module; - pa_core *core; - pa_socket_server*server; - pa_idxset *connections; -}; - -static void http_response(struct connection *c, int code, const char *msg, const char *mime) { - char s[256]; - assert(c); - assert(msg); - assert(mime); - - snprintf(s, sizeof(s), - "HTTP/1.0 %i %s\n" - "Connection: close\n" - "Content-Type: %s\n" - "Cache-Control: no-cache\n" - "Expires: 0\n" - "Server: "PACKAGE_NAME"/"PACKAGE_VERSION"\n" - "\n", code, msg, mime); - - pa_ioline_puts(c->line, s); -} - -static void http_message(struct connection *c, int code, const char *msg, const char *text) { - char s[256]; - assert(c); - - http_response(c, code, msg, "text/html"); - - if (!text) - text = msg; - - snprintf(s, sizeof(s), - "\n" - "\n" - "%s\n" - "%s\n", - text, text); - - pa_ioline_puts(c->line, s); - pa_ioline_defer_close(c->line); -} - - -static void connection_free(struct connection *c, int del) { - assert(c); - - if (c->url) - pa_xfree(c->url); - - if (del) - pa_idxset_remove_by_data(c->protocol->connections, c, NULL); - pa_ioline_unref(c->line); - pa_xfree(c); -} - -static void line_callback(pa_ioline *line, const char *s, void *userdata) { - struct connection *c = userdata; - assert(line); - assert(c); - - if (!s) { - /* EOF */ - connection_free(c, 1); - return; - } - - switch (c->state) { - case REQUEST_LINE: { - if (memcmp(s, "GET ", 4)) - goto fail; - - s +=4; - - c->url = pa_xstrndup(s, strcspn(s, " \r\n\t?")); - c->state = MIME_HEADER; - break; - - } - - case MIME_HEADER: { - - /* Ignore MIME headers */ - if (strcspn(s, " \r\n") != 0) - break; - - /* We're done */ - c->state = DATA; - - pa_log_info(__FILE__": request for %s\n", c->url); - - if (!strcmp(c->url, URL_ROOT)) { - char txt[256]; - http_response(c, 200, "OK", "text/html"); - - pa_ioline_puts(c->line, - "\n" - "\n" - ""PACKAGE_NAME" "PACKAGE_VERSION"\n" - "\n"); - - pa_ioline_puts(c->line, - "

"PACKAGE_NAME" "PACKAGE_VERSION"

\n" - ""); - -#define PRINTF_FIELD(a,b) pa_ioline_printf(c->line, "\n",(a),(b)) - - PRINTF_FIELD("User Name:", pa_get_user_name(txt, sizeof(txt))); - PRINTF_FIELD("Fully Qualified Domain Name:", pa_get_fqdn(txt, sizeof(txt))); - PRINTF_FIELD("Default Sample Specification:", pa_sample_spec_snprint(txt, sizeof(txt), &c->protocol->core->default_sample_spec)); - PRINTF_FIELD("Default Sink:", pa_namereg_get_default_sink_name(c->protocol->core)); - PRINTF_FIELD("Default Source:", pa_namereg_get_default_source_name(c->protocol->core)); - - pa_ioline_puts(c->line, "
%s%s
"); - - pa_ioline_puts(c->line, "

Click here for an extensive server status report.

"); - - pa_ioline_puts(c->line, "\n"); - - pa_ioline_defer_close(c->line); - } else if (!strcmp(c->url, URL_CSS)) { - http_response(c, 200, "OK", "text/css"); - - pa_ioline_puts(c->line, - "body { color: black; background-color: white; margin: 0.5cm; }\n" - "a:link, a:visited { color: #900000; }\n" - "p { margin-left: 0.5cm; margin-right: 0.5cm; }\n" - "h1 { color: #00009F; }\n" - "h2 { color: #00009F; }\n" - "ul { margin-left: .5cm; }\n" - "ol { margin-left: .5cm; }\n" - "pre { margin-left: .5cm; background-color: #f0f0f0; padding: 0.4cm;}\n" - ".grey { color: #afafaf; }\n" - "table { margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; }\n" - "td { padding-left:10px; padding-right:10px; }\n"); - - pa_ioline_defer_close(c->line); - } else if (!strcmp(c->url, URL_STATUS)) { - char *r; - - http_response(c, 200, "OK", "text/plain"); - r = pa_full_status_string(c->protocol->core); - pa_ioline_puts(c->line, r); - pa_xfree(r); - - pa_ioline_defer_close(c->line); - } else - http_message(c, 404, "Not Found", NULL); - - break; - } - - default: - ; - } - - return; - -fail: - internal_server_error(c); -} - -static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) { - pa_protocol_http *p = userdata; - struct connection *c; - assert(s && io && p); - - if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) { - pa_log_warn(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS); - pa_iochannel_free(io); - return; - } - - c = pa_xmalloc(sizeof(struct connection)); - c->protocol = p; - c->line = pa_ioline_new(io); - c->state = REQUEST_LINE; - c->url = NULL; - - pa_ioline_set_callback(c->line, line_callback, c); - pa_idxset_put(p->connections, c, NULL); -} - -pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) { - pa_protocol_http* p; - assert(core && server); - - p = pa_xmalloc(sizeof(pa_protocol_http)); - p->module = m; - p->core = core; - p->server = server; - p->connections = pa_idxset_new(NULL, NULL); - - pa_socket_server_set_callback(p->server, on_connection, p); - - return p; -} - -static void free_connection(void *p, PA_GCC_UNUSED void *userdata) { - assert(p); - connection_free(p, 0); -} - -void pa_protocol_http_free(pa_protocol_http *p) { - assert(p); - - pa_idxset_free(p->connections, free_connection, NULL); - pa_socket_server_unref(p->server); - pa_xfree(p); -} diff --git a/polyp/protocol-http.h b/polyp/protocol-http.h deleted file mode 100644 index 0a1855e9..00000000 --- a/polyp/protocol-http.h +++ /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" - -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/polyp/protocol-native.c b/polyp/protocol-native.c deleted file mode 100644 index b94903d9..00000000 --- a/polyp/protocol-native.c +++ /dev/null @@ -1,2216 +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 -#endif - -#include -#include -#include -#include - -#include "protocol-native.h" -#include "native-common.h" -#include "packet.h" -#include "client.h" -#include "source-output.h" -#include "sink-input.h" -#include "pstream.h" -#include "tagstruct.h" -#include "pdispatch.h" -#include "pstream-util.h" -#include "authkey.h" -#include "namereg.h" -#include "scache.h" -#include "xmalloc.h" -#include "util.h" -#include "subscribe.h" -#include "log.h" -#include "autoload.h" -#include "authkey-prop.h" -#include "strlist.h" -#include "props.h" - -/* Kick a client if it doesn't authenticate within this time */ -#define AUTH_TIMEOUT 5 - -/* Don't accept more connection than this */ -#define MAX_CONNECTIONS 10 - -struct connection; -struct pa_protocol_native; - -struct record_stream { - struct connection *connection; - uint32_t index; - pa_source_output *source_output; - pa_memblockq *memblockq; - size_t fragment_size; -}; - -struct playback_stream { - int type; - struct connection *connection; - uint32_t index; - pa_sink_input *sink_input; - pa_memblockq *memblockq; - size_t requested_bytes; - int drain_request; - uint32_t drain_tag; -}; - -struct upload_stream { - int type; - struct connection *connection; - uint32_t index; - pa_memchunk memchunk; - size_t length; - char *name; - pa_sample_spec sample_spec; - pa_channel_map channel_map; -}; - -struct output_stream { - int type; -}; - -enum { - UPLOAD_STREAM, - PLAYBACK_STREAM -}; - -struct connection { - int authorized; - pa_protocol_native *protocol; - pa_client *client; - pa_pstream *pstream; - pa_pdispatch *pdispatch; - pa_idxset *record_streams, *output_streams; - uint32_t rrobin_index; - pa_subscription *subscription; - pa_time_event *auth_timeout_event; -}; - -struct pa_protocol_native { - pa_module *module; - int public; - pa_core *core; - pa_socket_server *server; - pa_idxset *connections; - uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH]; - int auth_cookie_in_property; -}; - -static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk); -static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length); -static void sink_input_kill_cb(pa_sink_input *i); -static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i); - -static void request_bytes(struct playback_stream*s); - -static void source_output_kill_cb(pa_source_output *o); -static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk); -static pa_usec_t source_output_get_latency_cb(pa_source_output *o); - -static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_drain_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_lookup(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_stat(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_get_record_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_finish_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_play_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_remove_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_subscribe(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_set_volume(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_cork_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_flush_or_trigger_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_set_stream_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_load_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_add_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_remove_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_get_autoload_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_get_autoload_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); -static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); - -static const pa_pdispatch_callback command_table[PA_COMMAND_MAX] = { - [PA_COMMAND_ERROR] = NULL, - [PA_COMMAND_TIMEOUT] = NULL, - [PA_COMMAND_REPLY] = NULL, - [PA_COMMAND_CREATE_PLAYBACK_STREAM] = command_create_playback_stream, - [PA_COMMAND_DELETE_PLAYBACK_STREAM] = command_delete_stream, - [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = command_drain_playback_stream, - [PA_COMMAND_CREATE_RECORD_STREAM] = command_create_record_stream, - [PA_COMMAND_DELETE_RECORD_STREAM] = command_delete_stream, - [PA_COMMAND_AUTH] = command_auth, - [PA_COMMAND_REQUEST] = NULL, - [PA_COMMAND_EXIT] = command_exit, - [PA_COMMAND_SET_CLIENT_NAME] = command_set_client_name, - [PA_COMMAND_LOOKUP_SINK] = command_lookup, - [PA_COMMAND_LOOKUP_SOURCE] = command_lookup, - [PA_COMMAND_STAT] = command_stat, - [PA_COMMAND_GET_PLAYBACK_LATENCY] = command_get_playback_latency, - [PA_COMMAND_GET_RECORD_LATENCY] = command_get_record_latency, - [PA_COMMAND_CREATE_UPLOAD_STREAM] = command_create_upload_stream, - [PA_COMMAND_DELETE_UPLOAD_STREAM] = command_delete_stream, - [PA_COMMAND_FINISH_UPLOAD_STREAM] = command_finish_upload_stream, - [PA_COMMAND_PLAY_SAMPLE] = command_play_sample, - [PA_COMMAND_REMOVE_SAMPLE] = command_remove_sample, - [PA_COMMAND_GET_SINK_INFO] = command_get_info, - [PA_COMMAND_GET_SOURCE_INFO] = command_get_info, - [PA_COMMAND_GET_CLIENT_INFO] = command_get_info, - [PA_COMMAND_GET_MODULE_INFO] = command_get_info, - [PA_COMMAND_GET_SINK_INPUT_INFO] = command_get_info, - [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = command_get_info, - [PA_COMMAND_GET_SAMPLE_INFO] = command_get_info, - [PA_COMMAND_GET_SINK_INFO_LIST] = command_get_info_list, - [PA_COMMAND_GET_SOURCE_INFO_LIST] = command_get_info_list, - [PA_COMMAND_GET_MODULE_INFO_LIST] = command_get_info_list, - [PA_COMMAND_GET_CLIENT_INFO_LIST] = command_get_info_list, - [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = command_get_info_list, - [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = command_get_info_list, - [PA_COMMAND_GET_SAMPLE_INFO_LIST] = command_get_info_list, - [PA_COMMAND_GET_SERVER_INFO] = command_get_server_info, - [PA_COMMAND_SUBSCRIBE] = command_subscribe, - - [PA_COMMAND_SET_SINK_VOLUME] = command_set_volume, - [PA_COMMAND_SET_SINK_INPUT_VOLUME] = command_set_volume, - - [PA_COMMAND_CORK_PLAYBACK_STREAM] = command_cork_playback_stream, - [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = command_flush_or_trigger_playback_stream, - [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = command_flush_or_trigger_playback_stream, - [PA_COMMAND_PREBUF_PLAYBACK_STREAM] = command_flush_or_trigger_playback_stream, - - [PA_COMMAND_CORK_RECORD_STREAM] = command_cork_record_stream, - [PA_COMMAND_FLUSH_RECORD_STREAM] = command_flush_record_stream, - - [PA_COMMAND_SET_DEFAULT_SINK] = command_set_default_sink_or_source, - [PA_COMMAND_SET_DEFAULT_SOURCE] = command_set_default_sink_or_source, - [PA_COMMAND_SET_PLAYBACK_STREAM_NAME] = command_set_stream_name, - [PA_COMMAND_SET_RECORD_STREAM_NAME] = command_set_stream_name, - [PA_COMMAND_KILL_CLIENT] = command_kill, - [PA_COMMAND_KILL_SINK_INPUT] = command_kill, - [PA_COMMAND_KILL_SOURCE_OUTPUT] = command_kill, - [PA_COMMAND_LOAD_MODULE] = command_load_module, - [PA_COMMAND_UNLOAD_MODULE] = command_unload_module, - [PA_COMMAND_GET_AUTOLOAD_INFO] = command_get_autoload_info, - [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = command_get_autoload_info_list, - [PA_COMMAND_ADD_AUTOLOAD] = command_add_autoload, - [PA_COMMAND_REMOVE_AUTOLOAD] = command_remove_autoload, - -}; - -/* structure management */ - -static struct upload_stream* upload_stream_new( - struct connection *c, - const pa_sample_spec *ss, - const pa_channel_map *map, - const char *name, size_t length) { - - struct upload_stream *s; - assert(c && ss && name && length); - - s = pa_xmalloc(sizeof(struct upload_stream)); - s->type = UPLOAD_STREAM; - s->connection = c; - s->sample_spec = *ss; - s->channel_map = *map; - s->name = pa_xstrdup(name); - - s->memchunk.memblock = NULL; - s->memchunk.index = 0; - s->memchunk.length = 0; - - s->length = length; - - pa_idxset_put(c->output_streams, s, &s->index); - return s; -} - -static void upload_stream_free(struct upload_stream *o) { - assert(o && o->connection); - - pa_idxset_remove_by_data(o->connection->output_streams, o, NULL); - - pa_xfree(o->name); - - if (o->memchunk.memblock) - pa_memblock_unref(o->memchunk.memblock); - - pa_xfree(o); -} - -static struct record_stream* record_stream_new( - struct connection *c, - pa_source *source, - const pa_sample_spec *ss, - const pa_channel_map *map, - const char *name, - size_t maxlength, - size_t fragment_size) { - - struct record_stream *s; - pa_source_output *source_output; - size_t base; - assert(c && source && ss && name && maxlength); - - if (!(source_output = pa_source_output_new(source, __FILE__, name, ss, map, -1))) - return NULL; - - s = pa_xmalloc(sizeof(struct record_stream)); - s->connection = c; - s->source_output = source_output; - s->source_output->push = source_output_push_cb; - s->source_output->kill = source_output_kill_cb; - s->source_output->get_latency = source_output_get_latency_cb; - s->source_output->userdata = s; - s->source_output->owner = c->protocol->module; - s->source_output->client = c->client; - - s->memblockq = pa_memblockq_new(maxlength, 0, base = pa_frame_size(ss), 0, 0, c->protocol->core->memblock_stat); - assert(s->memblockq); - - s->fragment_size = (fragment_size/base)*base; - if (!s->fragment_size) - s->fragment_size = base; - - pa_idxset_put(c->record_streams, s, &s->index); - return s; -} - -static void record_stream_free(struct record_stream* r) { - assert(r && r->connection); - - pa_idxset_remove_by_data(r->connection->record_streams, r, NULL); - pa_source_output_disconnect(r->source_output); - pa_source_output_unref(r->source_output); - pa_memblockq_free(r->memblockq); - pa_xfree(r); -} - -static struct playback_stream* playback_stream_new( - struct connection *c, - pa_sink *sink, - const pa_sample_spec *ss, - const pa_channel_map *map, - const char *name, - size_t maxlength, - size_t tlength, - size_t prebuf, - size_t minreq, - pa_cvolume *volume) { - - struct playback_stream *s; - pa_sink_input *sink_input; - assert(c && sink && ss && name && maxlength); - - if (!(sink_input = pa_sink_input_new(sink, __FILE__, name, ss, map, 0, -1))) - return NULL; - - s = pa_xmalloc(sizeof(struct playback_stream)); - s->type = PLAYBACK_STREAM; - s->connection = c; - s->sink_input = sink_input; - - s->sink_input->peek = sink_input_peek_cb; - s->sink_input->drop = sink_input_drop_cb; - s->sink_input->kill = sink_input_kill_cb; - s->sink_input->get_latency = sink_input_get_latency_cb; - s->sink_input->userdata = s; - s->sink_input->owner = c->protocol->module; - s->sink_input->client = c->client; - - s->memblockq = pa_memblockq_new(maxlength, tlength, pa_frame_size(ss), prebuf, minreq, c->protocol->core->memblock_stat); - assert(s->memblockq); - - s->requested_bytes = 0; - s->drain_request = 0; - - s->sink_input->volume = *volume; - - pa_idxset_put(c->output_streams, s, &s->index); - return s; -} - -static void playback_stream_free(struct playback_stream* p) { - assert(p && p->connection); - - if (p->drain_request) - pa_pstream_send_error(p->connection->pstream, p->drain_tag, PA_ERROR_NOENTITY); - - pa_idxset_remove_by_data(p->connection->output_streams, p, NULL); - pa_sink_input_disconnect(p->sink_input); - pa_sink_input_unref(p->sink_input); - pa_memblockq_free(p->memblockq); - pa_xfree(p); -} - -static void connection_free(struct connection *c) { - struct record_stream *r; - struct output_stream *o; - assert(c && c->protocol); - - pa_idxset_remove_by_data(c->protocol->connections, c, NULL); - while ((r = pa_idxset_first(c->record_streams, NULL))) - record_stream_free(r); - pa_idxset_free(c->record_streams, NULL, NULL); - - while ((o = pa_idxset_first(c->output_streams, NULL))) - if (o->type == PLAYBACK_STREAM) - playback_stream_free((struct playback_stream*) o); - else - upload_stream_free((struct upload_stream*) o); - pa_idxset_free(c->output_streams, NULL, NULL); - - pa_pdispatch_unref(c->pdispatch); - pa_pstream_close(c->pstream); - pa_pstream_unref(c->pstream); - pa_client_free(c->client); - - if (c->subscription) - pa_subscription_free(c->subscription); - - if (c->auth_timeout_event) - c->protocol->core->mainloop->time_free(c->auth_timeout_event); - - pa_xfree(c); -} - -static void request_bytes(struct playback_stream *s) { - pa_tagstruct *t; - size_t l; - assert(s); - - if (!(l = pa_memblockq_missing(s->memblockq))) - return; - - if (l <= s->requested_bytes) - return; - - l -= s->requested_bytes; - - if (l < pa_memblockq_get_minreq(s->memblockq)) - return; - - s->requested_bytes += l; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_REQUEST); - pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ - pa_tagstruct_putu32(t, s->index); - pa_tagstruct_putu32(t, l); - pa_pstream_send_tagstruct(s->connection->pstream, t); - -/* pa_log(__FILE__": Requesting %u bytes\n", l); */ -} - -static void send_memblock(struct connection *c) { - uint32_t start; - struct record_stream *r; - - start = PA_IDXSET_INVALID; - for (;;) { - pa_memchunk chunk; - - if (!(r = pa_idxset_rrobin(c->record_streams, &c->rrobin_index))) - return; - - if (start == PA_IDXSET_INVALID) - start = c->rrobin_index; - else if (start == c->rrobin_index) - return; - - if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) { - pa_memchunk schunk = chunk; - - if (schunk.length > r->fragment_size) - schunk.length = r->fragment_size; - - pa_pstream_send_memblock(c->pstream, r->index, 0, &schunk); - pa_memblockq_drop(r->memblockq, &chunk, schunk.length); - pa_memblock_unref(schunk.memblock); - - return; - } - } -} - -static void send_playback_stream_killed(struct playback_stream *p) { - pa_tagstruct *t; - assert(p); - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_KILLED); - pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ - pa_tagstruct_putu32(t, p->index); - pa_pstream_send_tagstruct(p->connection->pstream, t); -} - -static void send_record_stream_killed(struct record_stream *r) { - pa_tagstruct *t; - assert(r); - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_KILLED); - pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ - pa_tagstruct_putu32(t, r->index); - pa_pstream_send_tagstruct(r->connection->pstream, t); -} - -/*** sinkinput callbacks ***/ - -static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) { - struct playback_stream *s; - assert(i && i->userdata && chunk); - s = i->userdata; - - if (pa_memblockq_peek(s->memblockq, chunk) < 0) - return -1; - - return 0; -} - -static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { - struct playback_stream *s; - assert(i && i->userdata && length); - s = i->userdata; - - pa_memblockq_drop(s->memblockq, chunk, length); - request_bytes(s); - - if (s->drain_request && !pa_memblockq_is_readable(s->memblockq)) { - pa_pstream_send_simple_ack(s->connection->pstream, s->drain_tag); - s->drain_request = 0; - } - -/* pa_log(__FILE__": after_drop: %u\n", pa_memblockq_get_length(s->memblockq)); */ -} - -static void sink_input_kill_cb(pa_sink_input *i) { - assert(i && i->userdata); - send_playback_stream_killed((struct playback_stream *) i->userdata); - playback_stream_free((struct playback_stream *) i->userdata); -} - -static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) { - struct playback_stream *s; - assert(i && i->userdata); - s = i->userdata; - - /*pa_log(__FILE__": get_latency: %u\n", pa_memblockq_get_length(s->memblockq));*/ - - return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec); -} - -/*** source_output callbacks ***/ - -static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { - struct record_stream *s; - assert(o && o->userdata && chunk); - s = o->userdata; - - pa_memblockq_push_align(s->memblockq, chunk, 0); - if (!pa_pstream_is_pending(s->connection->pstream)) - send_memblock(s->connection); -} - -static void source_output_kill_cb(pa_source_output *o) { - assert(o && o->userdata); - send_record_stream_killed((struct record_stream *) o->userdata); - record_stream_free((struct record_stream *) o->userdata); -} - -static pa_usec_t source_output_get_latency_cb(pa_source_output *o) { - struct record_stream *s; - assert(o && o->userdata); - s = o->userdata; - - /*pa_log(__FILE__": get_latency: %u\n", pa_memblockq_get_length(s->memblockq));*/ - - return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec); -} - -/*** pdispatch callbacks ***/ - -static void protocol_error(struct connection *c) { - pa_log(__FILE__": protocol error, kicking client\n"); - connection_free(c); -} - -static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - struct playback_stream *s; - size_t maxlength, tlength, prebuf, minreq; - uint32_t sink_index; - const char *name, *sink_name; - pa_sample_spec ss; - pa_channel_map map; - pa_tagstruct *reply; - pa_sink *sink; - pa_cvolume volume; - int corked; - - assert(c && t && c->protocol && c->protocol->core); - - if (pa_tagstruct_get( - t, - PA_TAG_STRING, &name, - PA_TAG_SAMPLE_SPEC, &ss, - PA_TAG_CHANNEL_MAP, &map, - PA_TAG_U32, &sink_index, - PA_TAG_STRING, &sink_name, - PA_TAG_U32, &maxlength, - PA_TAG_BOOLEAN, &corked, - PA_TAG_U32, &tlength, - PA_TAG_U32, &prebuf, - PA_TAG_U32, &minreq, - PA_TAG_CVOLUME, &volume, - PA_TAG_INVALID) < 0 || - !pa_tagstruct_eof(t) || - !name) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (sink_index != (uint32_t) -1) - sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index); - else - sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1); - - if (!sink) { - pa_log("%s: Can't find a suitable sink.\n", __FILE__); - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - if (!(s = playback_stream_new(c, sink, &ss, &map, name, maxlength, tlength, prebuf, minreq, &volume))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID); - return; - } - - pa_sink_input_cork(s->sink_input, corked); - - reply = pa_tagstruct_new(NULL, 0); - assert(reply); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - pa_tagstruct_putu32(reply, s->index); - assert(s->sink_input); - pa_tagstruct_putu32(reply, s->sink_input->index); - pa_tagstruct_putu32(reply, s->requested_bytes = pa_memblockq_missing(s->memblockq)); - pa_pstream_send_tagstruct(c->pstream, reply); - request_bytes(s); -} - -static void command_delete_stream(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t channel; - assert(c && t); - - if (pa_tagstruct_getu32(t, &channel) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (command == PA_COMMAND_DELETE_PLAYBACK_STREAM) { - struct playback_stream *s; - if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != PLAYBACK_STREAM)) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); - return; - } - - playback_stream_free(s); - } else if (command == PA_COMMAND_DELETE_RECORD_STREAM) { - struct record_stream *s; - if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); - return; - } - - record_stream_free(s); - } else { - struct upload_stream *s; - assert(command == PA_COMMAND_DELETE_UPLOAD_STREAM); - if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != UPLOAD_STREAM)) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); - return; - } - - upload_stream_free(s); - } - - pa_pstream_send_simple_ack(c->pstream, tag); -} - -static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - struct record_stream *s; - size_t maxlength, fragment_size; - uint32_t source_index; - const char *name, *source_name; - pa_sample_spec ss; - pa_channel_map map; - pa_tagstruct *reply; - pa_source *source; - int corked; - assert(c && t && c->protocol && c->protocol->core); - - if (pa_tagstruct_gets(t, &name) < 0 || !name || - pa_tagstruct_get_sample_spec(t, &ss) < 0 || - pa_tagstruct_get_channel_map(t, &map) < 0 || - pa_tagstruct_getu32(t, &source_index) < 0 || - pa_tagstruct_gets(t, &source_name) < 0 || - pa_tagstruct_getu32(t, &maxlength) < 0 || - pa_tagstruct_get_boolean(t, &corked) < 0 || - pa_tagstruct_getu32(t, &fragment_size) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (source_index != (uint32_t) -1) - source = pa_idxset_get_by_index(c->protocol->core->sources, source_index); - else - source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE, 1); - - if (!source) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - if (!(s = record_stream_new(c, source, &ss, &map, name, maxlength, fragment_size))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID); - return; - } - - pa_source_output_cork(s->source_output, corked); - - reply = pa_tagstruct_new(NULL, 0); - assert(reply); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - pa_tagstruct_putu32(reply, s->index); - assert(s->source_output); - pa_tagstruct_putu32(reply, s->source_output->index); - pa_pstream_send_tagstruct(c->pstream, reply); -} - -static void command_exit(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - assert(c && t); - - if (!pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop); - c->protocol->core->mainloop->quit(c->protocol->core->mainloop, 0); - pa_pstream_send_simple_ack(c->pstream, tag); /* nonsense */ - return; -} - -static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - const void*cookie; - assert(c && t); - - if (pa_tagstruct_get_arbitrary(t, &cookie, PA_NATIVE_COOKIE_LENGTH) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - if (memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) != 0) { - pa_log(__FILE__": Denied access to client with invalid authorization key.\n"); - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - c->authorized = 1; - if (c->auth_timeout_event) { - c->protocol->core->mainloop->time_free(c->auth_timeout_event); - c->auth_timeout_event = NULL; - } - } - - pa_pstream_send_simple_ack(c->pstream, tag); - return; -} - -static void command_set_client_name(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - const char *name; - assert(c && t); - - if (pa_tagstruct_gets(t, &name) < 0 || !name || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - pa_client_set_name(c->client, name); - pa_pstream_send_simple_ack(c->pstream, tag); - return; -} - -static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - const char *name; - uint32_t idx = PA_IDXSET_INVALID; - assert(c && t); - - if (pa_tagstruct_gets(t, &name) < 0 || !name || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (command == PA_COMMAND_LOOKUP_SINK) { - pa_sink *sink; - if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1))) - idx = sink->index; - } else { - pa_source *source; - assert(command == PA_COMMAND_LOOKUP_SOURCE); - if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1))) - idx = source->index; - } - - if (idx == PA_IDXSET_INVALID) - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - else { - pa_tagstruct *reply; - reply = pa_tagstruct_new(NULL, 0); - assert(reply); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - pa_tagstruct_putu32(reply, idx); - pa_pstream_send_tagstruct(c->pstream, reply); - } -} - -static void command_drain_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t idx; - struct playback_stream *s; - assert(c && t); - - if (pa_tagstruct_getu32(t, &idx) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (!(s = pa_idxset_get_by_index(c->output_streams, idx)) || s->type != PLAYBACK_STREAM) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - s->drain_request = 0; - - pa_memblockq_prebuf_disable(s->memblockq); - - if (!pa_memblockq_is_readable(s->memblockq)) { -/* pa_log("immediate drain: %u\n", pa_memblockq_get_length(s->memblockq)); */ - pa_pstream_send_simple_ack(c->pstream, tag); - } else { -/* pa_log("slow drain triggered\n"); */ - s->drain_request = 1; - s->drain_tag = tag; - - pa_sink_notify(s->sink_input->sink); - } -} - -static void command_stat(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - pa_tagstruct *reply; - assert(c && t); - - if (!pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - reply = pa_tagstruct_new(NULL, 0); - assert(reply); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - pa_tagstruct_putu32(reply, c->protocol->core->memblock_stat->total); - pa_tagstruct_putu32(reply, c->protocol->core->memblock_stat->total_size); - pa_tagstruct_putu32(reply, c->protocol->core->memblock_stat->allocated); - pa_tagstruct_putu32(reply, c->protocol->core->memblock_stat->allocated_size); - pa_tagstruct_putu32(reply, pa_scache_total_size(c->protocol->core)); - pa_pstream_send_tagstruct(c->pstream, reply); -} - -static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - pa_tagstruct *reply; - struct playback_stream *s; - struct timeval tv, now; - uint64_t counter; - uint32_t idx; - assert(c && t); - - if (pa_tagstruct_getu32(t, &idx) < 0 || - pa_tagstruct_get_timeval(t, &tv) < 0 || - pa_tagstruct_getu64(t, &counter) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (!(s = pa_idxset_get_by_index(c->output_streams, idx)) || s->type != PLAYBACK_STREAM) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - reply = pa_tagstruct_new(NULL, 0); - assert(reply); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - pa_tagstruct_put_usec(reply, pa_sink_input_get_latency(s->sink_input)); - pa_tagstruct_put_usec(reply, pa_sink_get_latency(s->sink_input->sink)); - pa_tagstruct_put_usec(reply, 0); - pa_tagstruct_put_boolean(reply, pa_memblockq_is_readable(s->memblockq)); - pa_tagstruct_putu32(reply, pa_memblockq_get_length(s->memblockq)); - pa_tagstruct_put_timeval(reply, &tv); - pa_gettimeofday(&now); - pa_tagstruct_put_timeval(reply, &now); - pa_tagstruct_putu64(reply, counter); - pa_pstream_send_tagstruct(c->pstream, reply); -} - -static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - pa_tagstruct *reply; - struct record_stream *s; - struct timeval tv, now; - uint64_t counter; - uint32_t idx; - assert(c && t); - - if (pa_tagstruct_getu32(t, &idx) < 0 || - pa_tagstruct_get_timeval(t, &tv) < 0 || - pa_tagstruct_getu64(t, &counter) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - reply = pa_tagstruct_new(NULL, 0); - assert(reply); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - pa_tagstruct_put_usec(reply, pa_source_output_get_latency(s->source_output)); - pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0); - pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source)); - pa_tagstruct_put_boolean(reply, 0); - pa_tagstruct_putu32(reply, pa_memblockq_get_length(s->memblockq)); - pa_tagstruct_put_timeval(reply, &tv); - pa_gettimeofday(&now); - pa_tagstruct_put_timeval(reply, &now); - pa_tagstruct_putu64(reply, counter); - pa_pstream_send_tagstruct(c->pstream, reply); -} - - -static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - struct upload_stream *s; - size_t length; - const char *name; - pa_sample_spec ss; - pa_channel_map map; - pa_tagstruct *reply; - assert(c && t && c->protocol && c->protocol->core); - - if (pa_tagstruct_gets(t, &name) < 0 || !name || - pa_tagstruct_get_sample_spec(t, &ss) < 0 || - pa_tagstruct_get_channel_map(t, &map) < 0 || - pa_tagstruct_getu32(t, &length) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if ((length % pa_frame_size(&ss)) != 0 || length <= 0 || !*name) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID); - return; - } - - if (!(s = upload_stream_new(c, &ss, &map, name, length))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID); - return; - } - - reply = pa_tagstruct_new(NULL, 0); - assert(reply); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - pa_tagstruct_putu32(reply, s->index); - pa_tagstruct_putu32(reply, length); - pa_pstream_send_tagstruct(c->pstream, reply); -} - -static void command_finish_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t channel; - struct upload_stream *s; - uint32_t idx; - assert(c && t); - - if (pa_tagstruct_getu32(t, &channel) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != UPLOAD_STREAM)) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); - return; - } - - pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, &idx); - pa_pstream_send_simple_ack(c->pstream, tag); - upload_stream_free(s); -} - -static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t sink_index; - pa_cvolume volume; - pa_sink *sink; - const char *name, *sink_name; - assert(c && t); - - if (pa_tagstruct_getu32(t, &sink_index) < 0 || - pa_tagstruct_gets(t, &sink_name) < 0 || - pa_tagstruct_get_cvolume(t, &volume) < 0 || - pa_tagstruct_gets(t, &name) < 0 || !name || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (sink_index != (uint32_t) -1) - sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index); - else - sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1); - - if (!sink) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - if (pa_scache_play_item(c->protocol->core, name, sink, &volume) < 0) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - pa_pstream_send_simple_ack(c->pstream, tag); -} - -static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - const char *name; - assert(c && t); - - if (pa_tagstruct_gets(t, &name) < 0 || !name || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (pa_scache_remove_item(c->protocol->core, name) < 0) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - pa_pstream_send_simple_ack(c->pstream, tag); -} - -static void sink_fill_tagstruct(pa_tagstruct *t, pa_sink *sink) { - assert(t && sink); - pa_tagstruct_put( - t, - PA_TAG_U32, sink->index, - PA_TAG_STRING, sink->name, - PA_TAG_STRING, sink->description, - PA_TAG_SAMPLE_SPEC, &sink->sample_spec, - PA_TAG_CHANNEL_MAP, &sink->channel_map, - PA_TAG_U32, sink->owner ? sink->owner->index : PA_INVALID_INDEX, - PA_TAG_CVOLUME, pa_sink_get_volume(sink, PA_MIXER_HARDWARE), - PA_TAG_U32, sink->monitor_source->index, - PA_TAG_STRING, sink->monitor_source->name, - PA_TAG_USEC, pa_sink_get_latency(sink), - PA_TAG_STRING, sink->driver, - PA_TAG_INVALID); -} - -static void source_fill_tagstruct(pa_tagstruct *t, pa_source *source) { - assert(t && source); - pa_tagstruct_putu32(t, source->index); - pa_tagstruct_puts(t, source->name); - pa_tagstruct_puts(t, source->description); - pa_tagstruct_put_sample_spec(t, &source->sample_spec); - pa_tagstruct_put_channel_map(t, &source->channel_map); - pa_tagstruct_putu32(t, source->owner ? source->owner->index : (uint32_t) -1); - pa_tagstruct_putu32(t, source->monitor_of ? source->monitor_of->index : (uint32_t) -1); - pa_tagstruct_puts(t, source->monitor_of ? source->monitor_of->name : NULL); - pa_tagstruct_put_usec(t, pa_source_get_latency(source)); - pa_tagstruct_puts(t, source->driver); -} - -static void client_fill_tagstruct(pa_tagstruct *t, pa_client *client) { - assert(t && client); - pa_tagstruct_putu32(t, client->index); - pa_tagstruct_puts(t, client->name); - pa_tagstruct_putu32(t, client->owner ? client->owner->index : (uint32_t) -1); - pa_tagstruct_puts(t, client->driver); -} - -static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) { - assert(t && module); - pa_tagstruct_putu32(t, module->index); - pa_tagstruct_puts(t, module->name); - pa_tagstruct_puts(t, module->argument); - pa_tagstruct_putu32(t, module->n_used); - pa_tagstruct_put_boolean(t, module->auto_unload); -} - -static void sink_input_fill_tagstruct(pa_tagstruct *t, pa_sink_input *s) { - assert(t && s); - pa_tagstruct_putu32(t, s->index); - pa_tagstruct_puts(t, s->name); - pa_tagstruct_putu32(t, s->owner ? s->owner->index : (uint32_t) -1); - pa_tagstruct_putu32(t, s->client ? s->client->index : (uint32_t) -1); - pa_tagstruct_putu32(t, s->sink->index); - pa_tagstruct_put_sample_spec(t, &s->sample_spec); - pa_tagstruct_put_channel_map(t, &s->channel_map); - pa_tagstruct_put_cvolume(t, &s->volume); - pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s)); - pa_tagstruct_put_usec(t, pa_sink_get_latency(s->sink)); - pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s))); - pa_tagstruct_puts(t, s->driver); -} - -static void source_output_fill_tagstruct(pa_tagstruct *t, pa_source_output *s) { - assert(t && s); - pa_tagstruct_putu32(t, s->index); - pa_tagstruct_puts(t, s->name); - pa_tagstruct_putu32(t, s->owner ? s->owner->index : (uint32_t) -1); - pa_tagstruct_putu32(t, s->client ? s->client->index : (uint32_t) -1); - pa_tagstruct_putu32(t, s->source->index); - pa_tagstruct_put_sample_spec(t, &s->sample_spec); - pa_tagstruct_put_channel_map(t, &s->channel_map); - pa_tagstruct_put_usec(t, pa_source_output_get_latency(s)); - pa_tagstruct_put_usec(t, pa_source_get_latency(s->source)); - pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s))); - pa_tagstruct_puts(t, s->driver); -} - -static void scache_fill_tagstruct(pa_tagstruct *t, pa_scache_entry *e) { - assert(t && e); - pa_tagstruct_putu32(t, e->index); - pa_tagstruct_puts(t, e->name); - pa_tagstruct_put_cvolume(t, &e->volume); - pa_tagstruct_put_usec(t, pa_bytes_to_usec(e->memchunk.length, &e->sample_spec)); - pa_tagstruct_put_sample_spec(t, &e->sample_spec); - pa_tagstruct_put_channel_map(t, &e->channel_map); - pa_tagstruct_putu32(t, e->memchunk.length); - pa_tagstruct_put_boolean(t, e->lazy); - pa_tagstruct_puts(t, e->filename); -} - -static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t idx; - pa_sink *sink = NULL; - pa_source *source = NULL; - pa_client *client = NULL; - pa_module *module = NULL; - pa_sink_input *si = NULL; - pa_source_output *so = NULL; - pa_scache_entry *sce = NULL; - const char *name; - pa_tagstruct *reply; - assert(c && t); - - - if (pa_tagstruct_getu32(t, &idx) < 0 || - (command != PA_COMMAND_GET_CLIENT_INFO && - command != PA_COMMAND_GET_MODULE_INFO && - command != PA_COMMAND_GET_SINK_INPUT_INFO && - command != PA_COMMAND_GET_SOURCE_OUTPUT_INFO && - pa_tagstruct_gets(t, &name) < 0) || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (command == PA_COMMAND_GET_SINK_INFO) { - if (idx != (uint32_t) -1) - sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); - else - sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1); - } else if (command == PA_COMMAND_GET_SOURCE_INFO) { - if (idx != (uint32_t) -1) - source = pa_idxset_get_by_index(c->protocol->core->sources, idx); - else - source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1); - } else if (command == PA_COMMAND_GET_CLIENT_INFO) - client = pa_idxset_get_by_index(c->protocol->core->clients, idx); - else if (command == PA_COMMAND_GET_MODULE_INFO) - module = pa_idxset_get_by_index(c->protocol->core->modules, idx); - else if (command == PA_COMMAND_GET_SINK_INPUT_INFO) - si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx); - else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO) - so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx); - else { - assert(command == PA_COMMAND_GET_SAMPLE_INFO); - if (idx != (uint32_t) -1) - sce = pa_idxset_get_by_index(c->protocol->core->scache, idx); - else - sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE, 0); - } - - if (!sink && !source && !client && !module && !si && !so && !sce) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - reply = pa_tagstruct_new(NULL, 0); - assert(reply); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - if (sink) - sink_fill_tagstruct(reply, sink); - else if (source) - source_fill_tagstruct(reply, source); - else if (client) - client_fill_tagstruct(reply, client); - else if (module) - module_fill_tagstruct(reply, module); - else if (si) - sink_input_fill_tagstruct(reply, si); - else if (so) - source_output_fill_tagstruct(reply, so); - else - scache_fill_tagstruct(reply, sce); - pa_pstream_send_tagstruct(c->pstream, reply); -} - -static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - pa_idxset *i; - uint32_t idx; - void *p; - pa_tagstruct *reply; - assert(c && t); - - if (!pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - reply = pa_tagstruct_new(NULL, 0); - assert(reply); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - - if (command == PA_COMMAND_GET_SINK_INFO_LIST) - i = c->protocol->core->sinks; - else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST) - i = c->protocol->core->sources; - else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST) - i = c->protocol->core->clients; - else if (command == PA_COMMAND_GET_MODULE_INFO_LIST) - i = c->protocol->core->modules; - else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST) - i = c->protocol->core->sink_inputs; - else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST) - i = c->protocol->core->source_outputs; - else { - assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST); - i = c->protocol->core->scache; - } - - if (i) { - for (p = pa_idxset_first(i, &idx); p; p = pa_idxset_next(i, &idx)) { - if (command == PA_COMMAND_GET_SINK_INFO_LIST) - sink_fill_tagstruct(reply, p); - else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST) - source_fill_tagstruct(reply, p); - else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST) - client_fill_tagstruct(reply, p); - else if (command == PA_COMMAND_GET_MODULE_INFO_LIST) - module_fill_tagstruct(reply, p); - else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST) - sink_input_fill_tagstruct(reply, p); - else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST) - source_output_fill_tagstruct(reply, p); - else { - assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST); - scache_fill_tagstruct(reply, p); - } - } - } - - pa_pstream_send_tagstruct(c->pstream, reply); -} - -static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - pa_tagstruct *reply; - char txt[256]; - const char *n; - assert(c && t); - - if (!pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - reply = pa_tagstruct_new(NULL, 0); - assert(reply); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - pa_tagstruct_puts(reply, PACKAGE_NAME); - pa_tagstruct_puts(reply, PACKAGE_VERSION); - pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt))); - pa_tagstruct_puts(reply, pa_get_fqdn(txt, sizeof(txt))); - pa_tagstruct_put_sample_spec(reply, &c->protocol->core->default_sample_spec); - - n = pa_namereg_get_default_sink_name(c->protocol->core); - pa_tagstruct_puts(reply, n); - n = pa_namereg_get_default_source_name(c->protocol->core); - pa_tagstruct_puts(reply, n); - - pa_tagstruct_putu32(reply, c->protocol->core->cookie); - - pa_pstream_send_tagstruct(c->pstream, reply); -} - -static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint32_t idx, void *userdata) { - pa_tagstruct *t; - struct connection *c = userdata; - assert(c && core); - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE_EVENT); - pa_tagstruct_putu32(t, (uint32_t) -1); - pa_tagstruct_putu32(t, e); - pa_tagstruct_putu32(t, idx); - pa_pstream_send_tagstruct(c->pstream, t); -} - -static void command_subscribe(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - pa_subscription_mask_t m; - assert(c && t); - - if (pa_tagstruct_getu32(t, &m) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (c->subscription) - pa_subscription_free(c->subscription); - - if (m != 0) { - c->subscription = pa_subscription_new(c->protocol->core, m, subscription_cb, c); - assert(c->subscription); - } else - c->subscription = NULL; - - pa_pstream_send_simple_ack(c->pstream, tag); -} - -static void command_set_volume(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t idx; - pa_cvolume volume; - pa_sink *sink = NULL; - pa_sink_input *si = NULL; - const char *name = NULL; - assert(c && t); - - if (pa_tagstruct_getu32(t, &idx) < 0 || - (command == PA_COMMAND_SET_SINK_VOLUME && pa_tagstruct_gets(t, &name) < 0) || - pa_tagstruct_get_cvolume(t, &volume) || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (command == PA_COMMAND_SET_SINK_VOLUME) { - if (idx != (uint32_t) -1) - sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); - else - sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1); - } else { - assert(command == PA_COMMAND_SET_SINK_INPUT_VOLUME); - si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx); - } - - if (!si && !sink) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - if (sink) - pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &volume); - else if (si) - pa_sink_input_set_volume(si, &volume); - - pa_pstream_send_simple_ack(c->pstream, tag); -} - -static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t idx; - int b; - struct playback_stream *s; - assert(c && t); - - if (pa_tagstruct_getu32(t, &idx) < 0 || - pa_tagstruct_get_boolean(t, &b) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (!(s = pa_idxset_get_by_index(c->output_streams, idx)) || s->type != PLAYBACK_STREAM) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - pa_sink_input_cork(s->sink_input, b); - pa_pstream_send_simple_ack(c->pstream, tag); -} - -static void command_flush_or_trigger_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t idx; - struct playback_stream *s; - assert(c && t); - - if (pa_tagstruct_getu32(t, &idx) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (!(s = pa_idxset_get_by_index(c->output_streams, idx)) || s->type != PLAYBACK_STREAM) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - if (command == PA_COMMAND_PREBUF_PLAYBACK_STREAM) - pa_memblockq_prebuf_reenable(s->memblockq); - else if (command == PA_COMMAND_TRIGGER_PLAYBACK_STREAM) - pa_memblockq_prebuf_disable(s->memblockq); - else { - assert(command == PA_COMMAND_FLUSH_PLAYBACK_STREAM); - pa_memblockq_flush(s->memblockq); - /*pa_log(__FILE__": flush: %u\n", pa_memblockq_get_length(s->memblockq));*/ - } - - pa_sink_notify(s->sink_input->sink); - pa_pstream_send_simple_ack(c->pstream, tag); - request_bytes(s); -} - -static void command_cork_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t idx; - struct record_stream *s; - int b; - assert(c && t); - - if (pa_tagstruct_getu32(t, &idx) < 0 || - pa_tagstruct_get_boolean(t, &b) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - pa_source_output_cork(s->source_output, b); - pa_pstream_send_simple_ack(c->pstream, tag); -} - -static void command_flush_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t idx; - struct record_stream *s; - assert(c && t); - - if (pa_tagstruct_getu32(t, &idx) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - pa_memblockq_flush(s->memblockq); - pa_pstream_send_simple_ack(c->pstream, tag); -} - -static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t idx; - const char *s; - assert(c && t); - - if (pa_tagstruct_getu32(t, &idx) < 0 || - pa_tagstruct_gets(t, &s) < 0 || !s || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - pa_namereg_set_default(c->protocol->core, s, command == PA_COMMAND_SET_DEFAULT_SOURCE ? PA_NAMEREG_SOURCE : PA_NAMEREG_SINK); - pa_pstream_send_simple_ack(c->pstream, tag); -} - -static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t idx; - const char *name; - assert(c && t); - - if (pa_tagstruct_getu32(t, &idx) < 0 || - pa_tagstruct_gets(t, &name) < 0 || !name || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (command == PA_COMMAND_SET_PLAYBACK_STREAM_NAME) { - struct playback_stream *s; - - if (!(s = pa_idxset_get_by_index(c->output_streams, idx)) || s->type != PLAYBACK_STREAM) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - pa_sink_input_set_name(s->sink_input, name); - - } else { - struct record_stream *s; - - if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - pa_source_output_set_name(s->source_output, name); - } - - pa_pstream_send_simple_ack(c->pstream, tag); -} - -static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t idx; - assert(c && t); - - if (pa_tagstruct_getu32(t, &idx) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (command == PA_COMMAND_KILL_CLIENT) { - pa_client *client; - - if (!(client = pa_idxset_get_by_index(c->protocol->core->clients, idx))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - pa_client_kill(client); - } else if (command == PA_COMMAND_KILL_SINK_INPUT) { - pa_sink_input *s; - - if (!(s = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - pa_sink_input_kill(s); - } else { - pa_source_output *s; - - assert(command == PA_COMMAND_KILL_SOURCE_OUTPUT); - - if (!(s = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - pa_source_output_kill(s); - } - - pa_pstream_send_simple_ack(c->pstream, tag); -} - -static void command_load_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - pa_module *m; - const char *name, *argument; - pa_tagstruct *reply; - assert(c && t); - - if (pa_tagstruct_gets(t, &name) < 0 || !name || - pa_tagstruct_gets(t, &argument) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (!(m = pa_module_load(c->protocol->core, name, argument))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_INITFAILED); - return; - } - - reply = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - pa_tagstruct_putu32(reply, m->index); - pa_pstream_send_tagstruct(c->pstream, reply); -} - -static void command_unload_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t idx; - pa_module *m; - assert(c && t); - - if (pa_tagstruct_getu32(t, &idx) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (!(m = pa_idxset_get_by_index(c->protocol->core->modules, idx))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - pa_module_unload_request(m); - pa_pstream_send_simple_ack(c->pstream, tag); -} - -static void command_add_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - const char *name, *module, *argument; - uint32_t type; - uint32_t idx; - pa_tagstruct *reply; - assert(c && t); - - if (pa_tagstruct_gets(t, &name) < 0 || !name || - pa_tagstruct_getu32(t, &type) < 0 || type > 1 || - pa_tagstruct_gets(t, &module) < 0 || !module || - pa_tagstruct_gets(t, &argument) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (pa_autoload_add(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, module, argument, &idx) < 0) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); - return; - } - - reply = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - pa_tagstruct_putu32(reply, idx); - pa_pstream_send_tagstruct(c->pstream, reply); -} - -static void command_remove_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - const char *name = NULL; - uint32_t type, idx = PA_IDXSET_INVALID; - int r; - assert(c && t); - - if ((pa_tagstruct_getu32(t, &idx) < 0 && - (pa_tagstruct_gets(t, &name) < 0 || - pa_tagstruct_getu32(t, &type) < 0)) || - (!name && idx == PA_IDXSET_INVALID) || - (name && type > 1) || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (name) - r = pa_autoload_remove_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE); - else - r = pa_autoload_remove_by_index(c->protocol->core, idx); - - if (r < 0) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - pa_pstream_send_simple_ack(c->pstream, tag); -} - -static void autoload_fill_tagstruct(pa_tagstruct *t, const pa_autoload_entry *e) { - assert(t && e); - - pa_tagstruct_putu32(t, e->index); - pa_tagstruct_puts(t, e->name); - pa_tagstruct_putu32(t, e->type == PA_NAMEREG_SINK ? 0 : 1); - pa_tagstruct_puts(t, e->module); - pa_tagstruct_puts(t, e->argument); -} - -static void command_get_autoload_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - const pa_autoload_entry *a = NULL; - uint32_t type, idx; - const char *name; - pa_tagstruct *reply; - assert(c && t); - - if ((pa_tagstruct_getu32(t, &idx) < 0 && - (pa_tagstruct_gets(t, &name) < 0 || - pa_tagstruct_getu32(t, &type) < 0)) || - (!name && idx == PA_IDXSET_INVALID) || - (name && type > 1) || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - - if (name) - a = pa_autoload_get_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE); - else - a = pa_autoload_get_by_index(c->protocol->core, idx); - - if (!a) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - reply = pa_tagstruct_new(NULL, 0); - assert(reply); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - autoload_fill_tagstruct(reply, a); - pa_pstream_send_tagstruct(c->pstream, reply); -} - -static void command_get_autoload_info_list(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - pa_tagstruct *reply; - assert(c && t); - - if (!pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - reply = pa_tagstruct_new(NULL, 0); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - - if (c->protocol->core->autoload_hashmap) { - pa_autoload_entry *a; - void *state = NULL; - - while ((a = pa_hashmap_iterate(c->protocol->core->autoload_hashmap, &state, NULL))) - autoload_fill_tagstruct(reply, a); - } - - pa_pstream_send_tagstruct(c->pstream, reply); -} - -/*** pstream callbacks ***/ - -static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) { - struct connection *c = userdata; - assert(p && packet && packet->data && c); - - if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) { - pa_log(__FILE__": invalid packet.\n"); - connection_free(c); - } -} - -static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata) { - struct connection *c = userdata; - struct output_stream *stream; - assert(p && chunk && userdata); - - if (!(stream = pa_idxset_get_by_index(c->output_streams, channel))) { - pa_log(__FILE__": client sent block for invalid stream.\n"); - connection_free(c); - return; - } - - if (stream->type == PLAYBACK_STREAM) { - struct playback_stream *ps = (struct playback_stream*) stream; - if (chunk->length >= ps->requested_bytes) - ps->requested_bytes = 0; - else - ps->requested_bytes -= chunk->length; - - pa_memblockq_push_align(ps->memblockq, chunk, delta); - assert(ps->sink_input); -/* pa_log(__FILE__": after_recv: %u\n", pa_memblockq_get_length(p->memblockq)); */ - - pa_sink_notify(ps->sink_input->sink); -/* pa_log(__FILE__": Recieved %u bytes.\n", chunk->length); */ - - } else { - struct upload_stream *u = (struct upload_stream*) stream; - size_t l; - assert(u->type == UPLOAD_STREAM); - - if (!u->memchunk.memblock) { - if (u->length == chunk->length) { - u->memchunk = *chunk; - pa_memblock_ref(u->memchunk.memblock); - u->length = 0; - } else { - u->memchunk.memblock = pa_memblock_new(u->length, c->protocol->core->memblock_stat); - u->memchunk.index = u->memchunk.length = 0; - } - } - - assert(u->memchunk.memblock); - - l = u->length; - if (l > chunk->length) - l = chunk->length; - - if (l > 0) { - memcpy((uint8_t*) u->memchunk.memblock->data + u->memchunk.index + u->memchunk.length, - (uint8_t*) chunk->memblock->data+chunk->index, l); - u->memchunk.length += l; - u->length -= l; - } - } -} - -static void pstream_die_callback(pa_pstream *p, void *userdata) { - struct connection *c = userdata; - assert(p && c); - connection_free(c); - -/* pa_log(__FILE__": connection died.\n");*/ -} - - -static void pstream_drain_callback(pa_pstream *p, void *userdata) { - struct connection *c = userdata; - assert(p && c); - - send_memblock(c); -} - -/*** client callbacks ***/ - -static void client_kill_cb(pa_client *c) { - assert(c && c->userdata); - connection_free(c->userdata); -} - -/*** socket server callbacks ***/ - -static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) { - struct connection *c = userdata; - assert(m && tv && c && c->auth_timeout_event == e); - - if (!c->authorized) - connection_free(c); -} - -static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, void *userdata) { - pa_protocol_native *p = userdata; - struct connection *c; - assert(io && p); - - if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) { - pa_log_warn(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS); - pa_iochannel_free(io); - return; - } - - c = pa_xmalloc(sizeof(struct connection)); - - c->authorized =!! p->public; - - if (!c->authorized) { - struct timeval tv; - pa_gettimeofday(&tv); - tv.tv_sec += AUTH_TIMEOUT; - c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c); - } else - c->auth_timeout_event = NULL; - - c->protocol = p; - assert(p->core); - c->client = pa_client_new(p->core, __FILE__, "Client"); - assert(c->client); - c->client->kill = client_kill_cb; - c->client->userdata = c; - c->client->owner = p->module; - - c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->memblock_stat); - assert(c->pstream); - - pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c); - pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c); - pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c); - pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c); - - c->pdispatch = pa_pdispatch_new(p->core->mainloop, command_table, PA_COMMAND_MAX); - assert(c->pdispatch); - - c->record_streams = pa_idxset_new(NULL, NULL); - c->output_streams = pa_idxset_new(NULL, NULL); - assert(c->record_streams && c->output_streams); - - c->rrobin_index = PA_IDXSET_INVALID; - c->subscription = NULL; - - pa_idxset_put(p->connections, c, NULL); -} - -/*** module entry points ***/ - -static int load_key(pa_protocol_native*p, const char*fn) { - assert(p); - - p->auth_cookie_in_property = 0; - - if (!fn && pa_authkey_prop_get(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0) { - pa_log_info(__FILE__": using already loaded auth cookie.\n"); - pa_authkey_prop_ref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME); - p->auth_cookie_in_property = 1; - return 0; - } - - if (!fn) - fn = PA_NATIVE_COOKIE_FILE; - - if (pa_authkey_load_auto(fn, p->auth_cookie, sizeof(p->auth_cookie)) < 0) - return -1; - - pa_log_info(__FILE__": loading cookie from disk.\n"); - - if (pa_authkey_prop_put(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0) - p->auth_cookie_in_property = 1; - - return 0; -} - -static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_modargs *ma) { - pa_protocol_native *p; - int public = 0; - assert(c && ma); - - if (pa_modargs_get_value_boolean(ma, "public", &public) < 0) { - pa_log(__FILE__": public= expects a boolean argument.\n"); - return NULL; - } - - p = pa_xmalloc(sizeof(pa_protocol_native)); - p->core = c; - p->module = m; - p->public = public; - p->server = NULL; - - if (load_key(p, pa_modargs_get_value(ma, "cookie", NULL)) < 0) { - pa_xfree(p); - return NULL; - } - - p->connections = pa_idxset_new(NULL, NULL); - assert(p->connections); - - return p; -} - -pa_protocol_native* pa_protocol_native_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma) { - char t[256]; - pa_protocol_native *p; - - if (!(p = protocol_new_internal(core, m, ma))) - return NULL; - - p->server = server; - pa_socket_server_set_callback(p->server, on_connection, p); - - if (pa_socket_server_get_address(p->server, t, sizeof(t))) { - pa_strlist *l; - l = pa_property_get(core, PA_NATIVE_SERVER_PROPERTY_NAME); - l = pa_strlist_prepend(l, t); - pa_property_replace(core, PA_NATIVE_SERVER_PROPERTY_NAME, l); - } - - return p; -} - -void pa_protocol_native_free(pa_protocol_native *p) { - struct connection *c; - assert(p); - - while ((c = pa_idxset_first(p->connections, NULL))) - connection_free(c); - pa_idxset_free(p->connections, NULL, NULL); - - if (p->server) { - char t[256]; - - if (pa_socket_server_get_address(p->server, t, sizeof(t))) { - pa_strlist *l; - l = pa_property_get(p->core, PA_NATIVE_SERVER_PROPERTY_NAME); - l = pa_strlist_remove(l, t); - - if (l) - pa_property_replace(p->core, PA_NATIVE_SERVER_PROPERTY_NAME, l); - else - pa_property_remove(p->core, PA_NATIVE_SERVER_PROPERTY_NAME); - } - - pa_socket_server_unref(p->server); - } - - if (p->auth_cookie_in_property) - pa_authkey_prop_unref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME); - - pa_xfree(p); -} - -pa_protocol_native* pa_protocol_native_new_iochannel(pa_core*core, pa_iochannel *io, pa_module *m, pa_modargs *ma) { - pa_protocol_native *p; - - if (!(p = protocol_new_internal(core, m, ma))) - return NULL; - - on_connection(NULL, io, p); - - return p; -} diff --git a/polyp/protocol-native.h b/polyp/protocol-native.h deleted file mode 100644 index 12e85d0b..00000000 --- a/polyp/protocol-native.h +++ /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" - -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/polyp/protocol-simple.c b/polyp/protocol-simple.c deleted file mode 100644 index 113919b3..00000000 --- a/polyp/protocol-simple.c +++ /dev/null @@ -1,453 +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 -#endif - -#include -#include -#include -#include -#include -#include - -#include "sink-input.h" -#include "source-output.h" -#include "protocol-simple.h" -#include "client.h" -#include "sample-util.h" -#include "namereg.h" -#include "xmalloc.h" -#include "log.h" - -/* Don't allow more than this many concurrent connections */ -#define MAX_CONNECTIONS 10 - -struct connection { - pa_protocol_simple *protocol; - pa_iochannel *io; - pa_sink_input *sink_input; - pa_source_output *source_output; - pa_client *client; - pa_memblockq *input_memblockq, *output_memblockq; - pa_defer_event *defer_event; - - struct { - pa_memblock *current_memblock; - size_t memblock_index, fragment_size; - } playback; -}; - -struct pa_protocol_simple { - pa_module *module; - pa_core *core; - pa_socket_server*server; - pa_idxset *connections; - enum { - RECORD = 1, - PLAYBACK = 2, - DUPLEX = 3 - } mode; - pa_sample_spec sample_spec; - char *source_name, *sink_name; -}; - -#define PLAYBACK_BUFFER_SECONDS (.5) -#define PLAYBACK_BUFFER_FRAGMENTS (10) -#define RECORD_BUFFER_SECONDS (5) -#define RECORD_BUFFER_FRAGMENTS (100) - -static void connection_free(struct connection *c) { - assert(c); - - pa_idxset_remove_by_data(c->protocol->connections, c, NULL); - - if (c->playback.current_memblock) - pa_memblock_unref(c->playback.current_memblock); - if (c->sink_input) { - pa_sink_input_disconnect(c->sink_input); - pa_sink_input_unref(c->sink_input); - } - if (c->source_output) { - pa_source_output_disconnect(c->source_output); - pa_source_output_unref(c->source_output); - } - if (c->client) - pa_client_free(c->client); - if (c->io) - pa_iochannel_free(c->io); - if (c->input_memblockq) - pa_memblockq_free(c->input_memblockq); - if (c->output_memblockq) - pa_memblockq_free(c->output_memblockq); - if (c->defer_event) - c->protocol->core->mainloop->defer_free(c->defer_event); - pa_xfree(c); -} - -static int do_read(struct connection *c) { - pa_memchunk chunk; - ssize_t r; - size_t l; - - if (!c->sink_input || !(l = pa_memblockq_missing(c->input_memblockq))) - return 0; - - if (l > c->playback.fragment_size) - l = c->playback.fragment_size; - - if (c->playback.current_memblock) - if (c->playback.current_memblock->length - c->playback.memblock_index < l) { - pa_memblock_unref(c->playback.current_memblock); - c->playback.current_memblock = NULL; - c->playback.memblock_index = 0; - } - - if (!c->playback.current_memblock) { - c->playback.current_memblock = pa_memblock_new(c->playback.fragment_size*2, c->protocol->core->memblock_stat); - assert(c->playback.current_memblock && c->playback.current_memblock->length >= l); - c->playback.memblock_index = 0; - } - - if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) { - pa_log(__FILE__": read() failed: %s\n", r == 0 ? "EOF" : strerror(errno)); - return -1; - } - - chunk.memblock = c->playback.current_memblock; - chunk.index = c->playback.memblock_index; - chunk.length = r; - assert(chunk.memblock); - - c->playback.memblock_index += r; - - assert(c->input_memblockq); - pa_memblockq_push_align(c->input_memblockq, &chunk, 0); - assert(c->sink_input); - pa_sink_notify(c->sink_input->sink); - - return 0; -} - -static int do_write(struct connection *c) { - pa_memchunk chunk; - ssize_t r; - - if (!c->source_output) - return 0; - - assert(c->output_memblockq); - if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) - return 0; - - assert(chunk.memblock && chunk.length); - - if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) { - pa_memblock_unref(chunk.memblock); - pa_log(__FILE__": write(): %s\n", strerror(errno)); - return -1; - } - - pa_memblockq_drop(c->output_memblockq, &chunk, r); - pa_memblock_unref(chunk.memblock); - - return 0; -} - - -static void do_work(struct connection *c) { - assert(c); - - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); - c->protocol->core->mainloop->defer_enable(c->defer_event, 0); - - if (pa_iochannel_is_writable(c->io)) - if (do_write(c) < 0) - goto fail; - - if (pa_iochannel_is_readable(c->io)) - if (do_read(c) < 0) - goto fail; - - if (pa_iochannel_is_hungup(c->io)) - c->protocol->core->mainloop->defer_enable(c->defer_event, 1); - - return; - -fail: - connection_free(c); -} - -/*** sink_input callbacks ***/ - -static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) { - struct connection*c; - assert(i && i->userdata && chunk); - c = i->userdata; - - if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) - return -1; - - return 0; -} - -static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { - struct connection*c = i->userdata; - assert(i && c && length); - - pa_memblockq_drop(c->input_memblockq, chunk, length); - - /* do something */ - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); - c->protocol->core->mainloop->defer_enable(c->defer_event, 1); -} - -static void sink_input_kill_cb(pa_sink_input *i) { - assert(i && i->userdata); - connection_free((struct connection *) i->userdata); -} - - -static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) { - struct connection*c = i->userdata; - assert(i && c); - return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); -} - -/*** source_output callbacks ***/ - -static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { - struct connection *c = o->userdata; - assert(o && c && chunk); - - pa_memblockq_push(c->output_memblockq, chunk, 0); - - /* do something */ - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); - c->protocol->core->mainloop->defer_enable(c->defer_event, 1); -} - -static void source_output_kill_cb(pa_source_output *o) { - assert(o && o->userdata); - connection_free((struct connection *) o->userdata); -} - -static pa_usec_t source_output_get_latency_cb(pa_source_output *o) { - struct connection*c = o->userdata; - assert(o && c); - return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec); -} - -/*** client callbacks ***/ - -static void client_kill_cb(pa_client *c) { - assert(c && c->userdata); - connection_free((struct connection *) c->userdata); -} - -/*** pa_iochannel callbacks ***/ - -static void io_callback(pa_iochannel*io, void *userdata) { - struct connection *c = userdata; - assert(io && c && c->io == io); - - do_work(c); -} - -/*** fixed callback ***/ - -static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) { - struct connection *c = userdata; - assert(a && c && c->defer_event == e); - - do_work(c); -} - -/*** socket_server callbacks ***/ - -static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) { - pa_protocol_simple *p = userdata; - struct connection *c = NULL; - char cname[256]; - assert(s && io && p); - - if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) { - pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS); - pa_iochannel_free(io); - return; - } - - c = pa_xmalloc(sizeof(struct connection)); - c->io = io; - c->sink_input = NULL; - c->source_output = NULL; - c->defer_event = NULL; - c->input_memblockq = c->output_memblockq = NULL; - c->protocol = p; - c->playback.current_memblock = NULL; - c->playback.memblock_index = 0; - c->playback.fragment_size = 0; - - pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); - c->client = pa_client_new(p->core, __FILE__, cname); - assert(c->client); - c->client->owner = p->module; - c->client->kill = client_kill_cb; - c->client->userdata = c; - - if (p->mode & PLAYBACK) { - pa_sink *sink; - size_t l; - - if (!(sink = pa_namereg_get(p->core, p->sink_name, PA_NAMEREG_SINK, 1))) { - pa_log(__FILE__": Failed to get sink.\n"); - goto fail; - } - - if (!(c->sink_input = pa_sink_input_new(sink, __FILE__, c->client->name, &p->sample_spec, NULL, 0, -1))) { - pa_log(__FILE__": Failed to create sink input.\n"); - goto fail; - } - - c->sink_input->owner = p->module; - c->sink_input->client = c->client; - - c->sink_input->peek = sink_input_peek_cb; - c->sink_input->drop = sink_input_drop_cb; - c->sink_input->kill = sink_input_kill_cb; - c->sink_input->get_latency = sink_input_get_latency_cb; - c->sink_input->userdata = c; - - l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS); - c->input_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&p->sample_spec), l/2, l/PLAYBACK_BUFFER_FRAGMENTS, p->core->memblock_stat); - assert(c->input_memblockq); - pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5); - c->playback.fragment_size = l/10; - } - - if (p->mode & RECORD) { - pa_source *source; - size_t l; - - if (!(source = pa_namereg_get(p->core, p->source_name, PA_NAMEREG_SOURCE, 1))) { - pa_log(__FILE__": Failed to get source.\n"); - goto fail; - } - - c->source_output = pa_source_output_new(source, __FILE__, c->client->name, &p->sample_spec, NULL, -1); - if (!c->source_output) { - pa_log(__FILE__": Failed to create source output.\n"); - goto fail; - } - c->source_output->owner = p->module; - c->source_output->client = c->client; - - c->source_output->push = source_output_push_cb; - c->source_output->kill = source_output_kill_cb; - c->source_output->get_latency = source_output_get_latency_cb; - c->source_output->userdata = c; - - l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS); - c->output_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&p->sample_spec), 0, 0, p->core->memblock_stat); - pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2); - } - - pa_iochannel_set_callback(c->io, io_callback, c); - pa_idxset_put(p->connections, c, NULL); - - c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c); - assert(c->defer_event); - p->core->mainloop->defer_enable(c->defer_event, 0); - - return; - -fail: - if (c) - connection_free(c); -} - -pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma) { - pa_protocol_simple* p = NULL; - int enable; - assert(core && server && ma); - - p = pa_xmalloc0(sizeof(pa_protocol_simple)); - p->module = m; - p->core = core; - p->server = server; - p->connections = pa_idxset_new(NULL, NULL); - - p->sample_spec = core->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &p->sample_spec) < 0) { - pa_log(__FILE__": Failed to parse sample type specification.\n"); - goto fail; - } - - p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL)); - p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); - - enable = 0; - if (pa_modargs_get_value_boolean(ma, "record", &enable) < 0) { - pa_log(__FILE__": record= expects a numeric argument.\n"); - goto fail; - } - p->mode = enable ? RECORD : 0; - - enable = 1; - if (pa_modargs_get_value_boolean(ma, "playback", &enable) < 0) { - pa_log(__FILE__": playback= expects a numeric argument.\n"); - goto fail; - } - p->mode |= enable ? PLAYBACK : 0; - - if ((p->mode & (RECORD|PLAYBACK)) == 0) { - pa_log(__FILE__": neither playback nor recording enabled for protocol.\n"); - goto fail; - } - - pa_socket_server_set_callback(p->server, on_connection, p); - - return p; - -fail: - if (p) - pa_protocol_simple_free(p); - return NULL; -} - - -void pa_protocol_simple_free(pa_protocol_simple *p) { - struct connection *c; - assert(p); - - if (p->connections) { - while((c = pa_idxset_first(p->connections, NULL))) - connection_free(c); - - pa_idxset_free(p->connections, NULL, NULL); - } - - if (p->server) - pa_socket_server_unref(p->server); - pa_xfree(p); -} - diff --git a/polyp/protocol-simple.h b/polyp/protocol-simple.h deleted file mode 100644 index 63455a53..00000000 --- a/polyp/protocol-simple.h +++ /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" - -typedef struct pa_protocol_simple pa_protocol_simple; - -pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma); -void pa_protocol_simple_free(pa_protocol_simple *n); - -#endif diff --git a/polyp/pstream-util.c b/polyp/pstream-util.c deleted file mode 100644 index ecd63d15..00000000 --- a/polyp/pstream-util.c +++ /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. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include "native-common.h" -#include "pstream-util.h" - -void pa_pstream_send_tagstruct(pa_pstream *p, pa_tagstruct *t) { - size_t length; - uint8_t *data; - pa_packet *packet; - assert(p); - assert(t); - - data = pa_tagstruct_free_data(t, &length); - assert(data && length); - packet = pa_packet_new_dynamic(data, length); - assert(packet); - pa_pstream_send_packet(p, packet); - pa_packet_unref(packet); -} - -void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error) { - pa_tagstruct *t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_ERROR); - pa_tagstruct_putu32(t, tag); - pa_tagstruct_putu32(t, error); - pa_pstream_send_tagstruct(p, t); -} - -void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag) { - pa_tagstruct *t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_REPLY); - pa_tagstruct_putu32(t, tag); - pa_pstream_send_tagstruct(p, t); -} diff --git a/polyp/pstream-util.h b/polyp/pstream-util.h deleted file mode 100644 index 601a9e99..00000000 --- a/polyp/pstream-util.h +++ /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 -#include "pstream.h" -#include "tagstruct.h" - -/* The tagstruct is freed!*/ -void pa_pstream_send_tagstruct(pa_pstream *p, pa_tagstruct *t); - -void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error); -void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag); - -#endif diff --git a/polyp/pstream.c b/polyp/pstream.c deleted file mode 100644 index bd00ba4f..00000000 --- a/polyp/pstream.c +++ /dev/null @@ -1,492 +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 -#endif - -#include -#include -#include -#include - -#ifdef HAVE_NETINET_IN_H -#include -#endif - -#include "winsock.h" - -#include "pstream.h" -#include "queue.h" -#include "xmalloc.h" -#include "log.h" - -typedef enum pa_pstream_descriptor_index { - PA_PSTREAM_DESCRIPTOR_LENGTH, - PA_PSTREAM_DESCRIPTOR_CHANNEL, - PA_PSTREAM_DESCRIPTOR_DELTA, - PA_PSTREAM_DESCRIPTOR_MAX -} pa_pstream_descriptor_index; - -typedef uint32_t pa_pstream_descriptor[PA_PSTREAM_DESCRIPTOR_MAX]; - -#define PA_PSTREAM_DESCRIPTOR_SIZE (PA_PSTREAM_DESCRIPTOR_MAX*sizeof(uint32_t)) -#define FRAME_SIZE_MAX (1024*500) /* half a megabyte */ - -struct item_info { - enum { PA_PSTREAM_ITEM_PACKET, PA_PSTREAM_ITEM_MEMBLOCK } type; - - /* memblock info */ - pa_memchunk chunk; - uint32_t channel; - uint32_t delta; - - /* packet info */ - pa_packet *packet; -}; - -struct pa_pstream { - int ref; - - pa_mainloop_api *mainloop; - pa_defer_event *defer_event; - pa_iochannel *io; - pa_queue *send_queue; - - int dead; - void (*die_callback) (pa_pstream *p, void *userdata); - void *die_callback_userdata; - - struct { - struct item_info* current; - pa_pstream_descriptor descriptor; - void *data; - size_t index; - } write; - - struct { - pa_memblock *memblock; - pa_packet *packet; - pa_pstream_descriptor descriptor; - void *data; - size_t index; - } read; - - void (*recieve_packet_callback) (pa_pstream *p, pa_packet *packet, void *userdata); - void *recieve_packet_callback_userdata; - - void (*recieve_memblock_callback) (pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata); - void *recieve_memblock_callback_userdata; - - void (*drain_callback)(pa_pstream *p, void *userdata); - void *drain_userdata; - - pa_memblock_stat *memblock_stat; -}; - -static void do_write(pa_pstream *p); -static void do_read(pa_pstream *p); - -static void do_something(pa_pstream *p) { - assert(p); - - p->mainloop->defer_enable(p->defer_event, 0); - - pa_pstream_ref(p); - - if (!p->dead && pa_iochannel_is_readable(p->io)) - do_read(p); - - if (!p->dead && pa_iochannel_is_writable(p->io)) - do_write(p); - - /* In case the line was hungup, make sure to rerun this function - as soon as possible, until all data has been read. */ - - if (!p->dead && pa_iochannel_is_hungup(p->io)) - p->mainloop->defer_enable(p->defer_event, 1); - - pa_pstream_unref(p); -} - -static void io_callback(pa_iochannel*io, void *userdata) { - pa_pstream *p = userdata; - assert(p && p->io == io); - do_something(p); -} - -static void defer_callback(pa_mainloop_api *m, pa_defer_event *e, void*userdata) { - pa_pstream *p = userdata; - assert(p && p->defer_event == e && p->mainloop == m); - do_something(p); -} - -pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_memblock_stat *s) { - pa_pstream *p; - assert(io); - - p = pa_xmalloc(sizeof(pa_pstream)); - p->ref = 1; - p->io = io; - pa_iochannel_set_callback(io, io_callback, p); - - p->dead = 0; - p->die_callback = NULL; - p->die_callback_userdata = NULL; - - p->mainloop = m; - p->defer_event = m->defer_new(m, defer_callback, p); - m->defer_enable(p->defer_event, 0); - - p->send_queue = pa_queue_new(); - assert(p->send_queue); - - p->write.current = NULL; - p->write.index = 0; - - p->read.memblock = NULL; - p->read.packet = NULL; - p->read.index = 0; - - p->recieve_packet_callback = NULL; - p->recieve_packet_callback_userdata = NULL; - - p->recieve_memblock_callback = NULL; - p->recieve_memblock_callback_userdata = NULL; - - p->drain_callback = NULL; - p->drain_userdata = NULL; - - p->memblock_stat = s; - - pa_iochannel_socket_set_rcvbuf(io, 1024*8); - pa_iochannel_socket_set_sndbuf(io, 1024*8); - - return p; -} - -static void item_free(void *item, PA_GCC_UNUSED void *p) { - struct item_info *i = item; - assert(i); - - if (i->type == PA_PSTREAM_ITEM_MEMBLOCK) { - assert(i->chunk.memblock); - pa_memblock_unref(i->chunk.memblock); - } else { - assert(i->type == PA_PSTREAM_ITEM_PACKET); - assert(i->packet); - pa_packet_unref(i->packet); - } - - pa_xfree(i); -} - -static void pstream_free(pa_pstream *p) { - assert(p); - - pa_pstream_close(p); - - pa_queue_free(p->send_queue, item_free, NULL); - - if (p->write.current) - item_free(p->write.current, NULL); - - if (p->read.memblock) - pa_memblock_unref(p->read.memblock); - - if (p->read.packet) - pa_packet_unref(p->read.packet); - - pa_xfree(p); -} - -void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet) { - struct item_info *i; - assert(p && packet && p->ref >= 1); - - if (p->dead) - return; - -/* pa_log(__FILE__": push-packet %p\n", packet); */ - - i = pa_xmalloc(sizeof(struct item_info)); - i->type = PA_PSTREAM_ITEM_PACKET; - i->packet = pa_packet_ref(packet); - - pa_queue_push(p->send_queue, i); - p->mainloop->defer_enable(p->defer_event, 1); -} - -void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk) { - struct item_info *i; - assert(p && channel != (uint32_t) -1 && chunk && p->ref >= 1); - - if (p->dead) - return; - -/* pa_log(__FILE__": push-memblock %p\n", chunk); */ - - i = pa_xmalloc(sizeof(struct item_info)); - i->type = PA_PSTREAM_ITEM_MEMBLOCK; - i->chunk = *chunk; - i->channel = channel; - i->delta = delta; - - pa_memblock_ref(i->chunk.memblock); - - pa_queue_push(p->send_queue, i); - p->mainloop->defer_enable(p->defer_event, 1); -} - -void pa_pstream_set_recieve_packet_callback(pa_pstream *p, void (*callback) (pa_pstream *p, pa_packet *packet, void *userdata), void *userdata) { - assert(p && callback); - - p->recieve_packet_callback = callback; - p->recieve_packet_callback_userdata = userdata; -} - -void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, void (*callback) (pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata), void *userdata) { - assert(p && callback); - - p->recieve_memblock_callback = callback; - p->recieve_memblock_callback_userdata = userdata; -} - -static void prepare_next_write_item(pa_pstream *p) { - assert(p); - - if (!(p->write.current = pa_queue_pop(p->send_queue))) - return; - - p->write.index = 0; - - if (p->write.current->type == PA_PSTREAM_ITEM_PACKET) { - /*pa_log(__FILE__": pop-packet %p\n", p->write.current->packet);*/ - - assert(p->write.current->packet); - p->write.data = p->write.current->packet->data; - p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->packet->length); - p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1); - p->write.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA] = 0; - } else { - assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK && p->write.current->chunk.memblock); - p->write.data = (uint8_t*) p->write.current->chunk.memblock->data + p->write.current->chunk.index; - p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->chunk.length); - p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl(p->write.current->channel); - p->write.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA] = htonl(p->write.current->delta); - } -} - -static void do_write(pa_pstream *p) { - void *d; - size_t l; - ssize_t r; - assert(p); - - if (!p->write.current) - prepare_next_write_item(p); - - if (!p->write.current) - return; - - assert(p->write.data); - - if (p->write.index < PA_PSTREAM_DESCRIPTOR_SIZE) { - d = (uint8_t*) p->write.descriptor + p->write.index; - l = PA_PSTREAM_DESCRIPTOR_SIZE - p->write.index; - } else { - d = (uint8_t*) p->write.data + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE; - l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE); - } - - if ((r = pa_iochannel_write(p->io, d, l)) < 0) - goto die; - - p->write.index += r; - - if (p->write.index >= PA_PSTREAM_DESCRIPTOR_SIZE+ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])) { - assert(p->write.current); - item_free(p->write.current, (void *) 1); - p->write.current = NULL; - - if (p->drain_callback && !pa_pstream_is_pending(p)) - p->drain_callback(p, p->drain_userdata); - } - - return; - -die: - p->dead = 1; - if (p->die_callback) - p->die_callback(p, p->die_callback_userdata); -} - -static void do_read(pa_pstream *p) { - void *d; - size_t l; - ssize_t r; - assert(p); - - if (p->read.index < PA_PSTREAM_DESCRIPTOR_SIZE) { - d = (uint8_t*) p->read.descriptor + p->read.index; - l = PA_PSTREAM_DESCRIPTOR_SIZE - p->read.index; - } else { - assert(p->read.data); - d = (uint8_t*) p->read.data + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE; - l = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE); - } - - if ((r = pa_iochannel_read(p->io, d, l)) <= 0) - goto die; - - p->read.index += r; - - if (p->read.index == PA_PSTREAM_DESCRIPTOR_SIZE) { - /* Reading of frame descriptor complete */ - - /* Frame size too large */ - if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) > FRAME_SIZE_MAX) { - pa_log(__FILE__": Frame size too large\n"); - goto die; - } - - assert(!p->read.packet && !p->read.memblock); - - if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]) == (uint32_t) -1) { - /* Frame is a packet frame */ - p->read.packet = pa_packet_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])); - assert(p->read.packet); - p->read.data = p->read.packet->data; - } else { - /* Frame is a memblock frame */ - p->read.memblock = pa_memblock_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]), p->memblock_stat); - assert(p->read.memblock); - p->read.data = p->read.memblock->data; - } - - } else if (p->read.index > PA_PSTREAM_DESCRIPTOR_SIZE) { - /* Frame payload available */ - - if (p->read.memblock && p->recieve_memblock_callback) { /* Is this memblock data? Than pass it to the user */ - l = (p->read.index - r) < PA_PSTREAM_DESCRIPTOR_SIZE ? p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE : (size_t) r; - - if (l > 0) { - pa_memchunk chunk; - - chunk.memblock = p->read.memblock; - chunk.index = p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE - l; - chunk.length = l; - - if (p->recieve_memblock_callback) - p->recieve_memblock_callback( - p, - ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]), - ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA]), - &chunk, - p->recieve_memblock_callback_userdata); - } - } - - /* Frame complete */ - if (p->read.index >= ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) + PA_PSTREAM_DESCRIPTOR_SIZE) { - if (p->read.memblock) { - assert(!p->read.packet); - - pa_memblock_unref(p->read.memblock); - p->read.memblock = NULL; - } else { - assert(p->read.packet); - - if (p->recieve_packet_callback) - p->recieve_packet_callback(p, p->read.packet, p->recieve_packet_callback_userdata); - - pa_packet_unref(p->read.packet); - p->read.packet = NULL; - } - - p->read.index = 0; - } - } - - return; - -die: - p->dead = 1; - if (p->die_callback) - p->die_callback(p, p->die_callback_userdata); - -} - -void pa_pstream_set_die_callback(pa_pstream *p, void (*callback)(pa_pstream *p, void *userdata), void *userdata) { - assert(p && callback); - p->die_callback = callback; - p->die_callback_userdata = userdata; -} - -int pa_pstream_is_pending(pa_pstream *p) { - assert(p); - - if (p->dead) - return 0; - - return p->write.current || !pa_queue_is_empty(p->send_queue); -} - -void pa_pstream_set_drain_callback(pa_pstream *p, void (*cb)(pa_pstream *p, void *userdata), void *userdata) { - assert(p); - - p->drain_callback = cb; - p->drain_userdata = userdata; -} - -void pa_pstream_unref(pa_pstream*p) { - assert(p && p->ref >= 1); - - if (!(--(p->ref))) - pstream_free(p); -} - -pa_pstream* pa_pstream_ref(pa_pstream*p) { - assert(p && p->ref >= 1); - p->ref++; - return p; -} - -void pa_pstream_close(pa_pstream *p) { - assert(p); - - p->dead = 1; - - if (p->io) { - pa_iochannel_free(p->io); - p->io = NULL; - } - - if (p->defer_event) { - p->mainloop->defer_free(p->defer_event); - p->defer_event = NULL; - } - - p->die_callback = NULL; - p->drain_callback = NULL; - p->recieve_packet_callback = NULL; - p->recieve_memblock_callback = NULL; -} diff --git a/polyp/pstream.h b/polyp/pstream.h deleted file mode 100644 index 77c92802..00000000 --- a/polyp/pstream.h +++ /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 - -#include "packet.h" -#include "memblock.h" -#include "iochannel.h" -#include "mainloop-api.h" -#include "memchunk.h" - -typedef struct pa_pstream pa_pstream; - -pa_pstream* pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_memblock_stat *s); -void pa_pstream_unref(pa_pstream*p); -pa_pstream* pa_pstream_ref(pa_pstream*p); - -void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet); -void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk); - -void pa_pstream_set_recieve_packet_callback(pa_pstream *p, void (*callback) (pa_pstream *p, pa_packet *packet, void *userdata), void *userdata); -void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, void (*callback) (pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata), void *userdata); -void pa_pstream_set_drain_callback(pa_pstream *p, void (*cb)(pa_pstream *p, void *userdata), void *userdata); - -void pa_pstream_set_die_callback(pa_pstream *p, void (*callback)(pa_pstream *p, void *userdata), void *userdata); - -int pa_pstream_is_pending(pa_pstream *p); - -void pa_pstream_close(pa_pstream *p); - -#endif diff --git a/polyp/queue.c b/polyp/queue.c deleted file mode 100644 index 80ec0068..00000000 --- a/polyp/queue.c +++ /dev/null @@ -1,108 +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 -#endif - -#include -#include - -#include "queue.h" -#include "xmalloc.h" - -struct queue_entry { - struct queue_entry *next; - void *data; -}; - -struct pa_queue { - struct queue_entry *front, *back; - unsigned length; -}; - -pa_queue* pa_queue_new(void) { - pa_queue *q = pa_xnew(pa_queue, 1); - q->front = q->back = NULL; - q->length = 0; - return q; -} - -void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata) { - struct queue_entry *e; - assert(q); - - e = q->front; - while (e) { - struct queue_entry *n = e->next; - - if (destroy) - destroy(e->data, userdata); - - pa_xfree(e); - e = n; - } - - pa_xfree(q); -} - -void pa_queue_push(pa_queue *q, void *p) { - struct queue_entry *e; - - e = pa_xnew(struct queue_entry, 1); - e->data = p; - e->next = NULL; - - if (q->back) - q->back->next = e; - else { - assert(!q->front); - q->front = e; - } - - q->back = e; - q->length++; -} - -void* pa_queue_pop(pa_queue *q) { - void *p; - struct queue_entry *e; - assert(q); - - if (!(e = q->front)) - return NULL; - - q->front = e->next; - if (q->back == e) - q->back = NULL; - - p = e->data; - pa_xfree(e); - - q->length--; - - return p; -} - -int pa_queue_is_empty(pa_queue *q) { - assert(q); - return q->length == 0; -} diff --git a/polyp/queue.h b/polyp/queue.h deleted file mode 100644 index 3edcfb63..00000000 --- a/polyp/queue.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef fooqueuehfoo -#define fooqueuehfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -typedef struct pa_queue pa_queue; - -/* A simple implementation of the abstract data type queue. Stores - * pointers as members. The memory has to be managed by the caller. */ - -pa_queue* pa_queue_new(void); - -/* Free the queue and run the specified callback function for every remaining entry. The callback function may be NULL. */ -void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata); - -void pa_queue_push(pa_queue *q, void *p); -void* pa_queue_pop(pa_queue *q); - -int pa_queue_is_empty(pa_queue *q); - -#endif diff --git a/polyp/random.c b/polyp/random.c deleted file mode 100644 index 12f27bfd..00000000 --- a/polyp/random.c +++ /dev/null @@ -1,71 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "random.h" -#include "util.h" -#include "log.h" - -#ifndef OS_IS_WIN32 -#define RANDOM_DEVICE "/dev/urandom" -#endif - -void pa_random(void *ret_data, size_t length) { - int fd; - ssize_t r = 0; - assert(ret_data && length); - -#ifdef RANDOM_DEVICE - if ((fd = open(RANDOM_DEVICE, O_RDONLY)) >= 0) { - - if ((r = pa_loop_read(fd, ret_data, length)) < 0 || (size_t) r != length) - pa_log_error(__FILE__": failed to read entropy from '%s'\n", RANDOM_DEVICE); - - close(fd); - } -#endif - - if ((size_t) r != length) { - uint8_t *p; - size_t l; - -#ifdef RANDOM_DEVICE - pa_log_warn(__FILE__": WARNING: Failed to open entropy device '"RANDOM_DEVICE"': %s" - ", falling back to unsecure pseudo RNG.\n", strerror(errno)); -#endif - - srand(time(NULL)); - - for (p = ret_data, l = length; l > 0; p++, l--) - *p = (uint8_t) rand(); - } -} diff --git a/polyp/random.h b/polyp/random.h deleted file mode 100644 index bfb3df08..00000000 --- a/polyp/random.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef foorandomhfoo -#define foorandomhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -void pa_random(void *ret_data, size_t length); - -#endif diff --git a/polyp/resampler.c b/polyp/resampler.c deleted file mode 100644 index 0417e44e..00000000 --- a/polyp/resampler.c +++ /dev/null @@ -1,614 +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 -#endif - -#include -#include - -#include -#include -#include - -#include "resampler.h" -#include "sconv.h" -#include "xmalloc.h" -#include "log.h" - -struct pa_resampler { - pa_resample_method_t resample_method; - pa_sample_spec i_ss, o_ss; - pa_channel_map i_cm, o_cm; - size_t i_fz, o_fz; - pa_memblock_stat *memblock_stat; - - void (*impl_free)(pa_resampler *r); - void (*impl_update_input_rate)(pa_resampler *r, uint32_t rate); - void (*impl_run)(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out); - void *impl_data; -}; - -struct impl_libsamplerate { - float* buf1, *buf2, *buf3, *buf4; - unsigned buf1_samples, buf2_samples, buf3_samples, buf4_samples; - - pa_convert_to_float32ne_func_t to_float32ne_func; - pa_convert_from_float32ne_func_t from_float32ne_func; - SRC_STATE *src_state; - - int map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX]; - int map_required; -}; - -struct impl_trivial { - unsigned o_counter; - unsigned i_counter; -}; - -static int libsamplerate_init(pa_resampler*r); -static int trivial_init(pa_resampler*r); - -pa_resampler* pa_resampler_new( - const pa_sample_spec *a, - const pa_channel_map *am, - const pa_sample_spec *b, - const pa_channel_map *bm, - pa_memblock_stat *s, - pa_resample_method_t resample_method) { - - pa_resampler *r = NULL; - - assert(a); - assert(b); - assert(pa_sample_spec_valid(a)); - assert(pa_sample_spec_valid(b)); - assert(resample_method != PA_RESAMPLER_INVALID); - - r = pa_xnew(pa_resampler, 1); - r->impl_data = NULL; - r->memblock_stat = s; - r->resample_method = resample_method; - - r->impl_free = NULL; - r->impl_update_input_rate = NULL; - r->impl_run = NULL; - - /* Fill sample specs */ - r->i_ss = *a; - r->o_ss = *b; - - if (am) - r->i_cm = *am; - else - pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels); - - if (bm) - r->o_cm = *bm; - else - pa_channel_map_init_auto(&r->o_cm, r->o_ss.channels); - - r->i_fz = pa_frame_size(a); - r->o_fz = pa_frame_size(b); - - /* Choose implementation */ - if (a->channels != b->channels || - a->format != b->format || - !pa_channel_map_equal(&r->i_cm, &r->o_cm) || - resample_method != PA_RESAMPLER_TRIVIAL) { - - /* Use the libsamplerate based resampler for the complicated cases */ - if (resample_method == PA_RESAMPLER_TRIVIAL) - r->resample_method = PA_RESAMPLER_SRC_ZERO_ORDER_HOLD; - - if (libsamplerate_init(r) < 0) - goto fail; - - } else { - /* Use our own simple non-fp resampler for the trivial cases and when the user selects it */ - if (trivial_init(r) < 0) - goto fail; - } - - return r; - -fail: - if (r) - pa_xfree(r); - - return NULL; -} - -void pa_resampler_free(pa_resampler *r) { - assert(r); - - if (r->impl_free) - r->impl_free(r); - - pa_xfree(r); -} - -void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate) { - assert(r); - assert(rate > 0); - - if (r->i_ss.rate == rate) - return; - - r->i_ss.rate = rate; - - if (r->impl_update_input_rate) - r->impl_update_input_rate(r, rate); -} - -void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) { - assert(r && in && out && r->impl_run); - - r->impl_run(r, in, out); -} - -size_t pa_resampler_request(pa_resampler *r, size_t out_length) { - assert(r && (out_length % r->o_fz) == 0); - return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz; -} - -pa_resample_method_t pa_resampler_get_method(pa_resampler *r) { - assert(r); - return r->resample_method; -} - -static const char * const resample_methods[] = { - "src-sinc-best-quality", - "src-sinc-medium-quality", - "src-sinc-fastest", - "src-zero-order-hold", - "src-linear", - "trivial" -}; - -const char *pa_resample_method_to_string(pa_resample_method_t m) { - - if (m < 0 || m >= PA_RESAMPLER_MAX) - return NULL; - - return resample_methods[m]; -} - -pa_resample_method_t pa_parse_resample_method(const char *string) { - pa_resample_method_t m; - - assert(string); - - for (m = 0; m < PA_RESAMPLER_MAX; m++) - if (!strcmp(string, resample_methods[m])) - return m; - - return PA_RESAMPLER_INVALID; -} - - -/*** libsamplerate based implementation ***/ - -static void libsamplerate_free(pa_resampler *r) { - struct impl_libsamplerate *u; - - assert(r); - assert(r->impl_data); - - u = r->impl_data; - - if (u->src_state) - src_delete(u->src_state); - - pa_xfree(u->buf1); - pa_xfree(u->buf2); - pa_xfree(u->buf3); - pa_xfree(u->buf4); - pa_xfree(u); -} - -static void calc_map_table(pa_resampler *r) { - struct impl_libsamplerate *u; - unsigned oc; - assert(r); - assert(r->impl_data); - - u = r->impl_data; - - if (!(u->map_required = (!pa_channel_map_equal(&r->i_cm, &r->o_cm) || r->i_ss.channels != r->o_ss.channels))) - return; - - for (oc = 0; oc < r->o_ss.channels; oc++) { - unsigned ic, i = 0; - - for (ic = 0; ic < r->i_ss.channels; ic++) { - pa_channel_position_t a, b; - - a = r->i_cm.map[ic]; - b = r->o_cm.map[oc]; - - if (a == b || - (a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_LEFT) || - (a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_RIGHT) || - (a == PA_CHANNEL_POSITION_LEFT && b == PA_CHANNEL_POSITION_MONO) || - (a == PA_CHANNEL_POSITION_RIGHT && b == PA_CHANNEL_POSITION_MONO)) - - u->map_table[oc][i++] = ic; - } - - /* Add an end marker */ - if (i < PA_CHANNELS_MAX) - u->map_table[oc][i] = -1; - } -} - -static float * convert_to_float(pa_resampler *r, float *input, unsigned n_frames) { - struct impl_libsamplerate *u; - unsigned n_samples; - - assert(r); - assert(input); - assert(r->impl_data); - u = r->impl_data; - - /* Convert the incoming sample into floats and place them in buf1 */ - - if (!u->to_float32ne_func) - return input; - - n_samples = n_frames * r->i_ss.channels; - - if (u->buf1_samples < n_samples) - u->buf1 = pa_xrealloc(u->buf1, sizeof(float) * (u->buf1_samples = n_samples)); - - u->to_float32ne_func(n_samples, input, u->buf1); - - return u->buf1; -} - -static float *remap_channels(pa_resampler *r, float *input, unsigned n_frames) { - struct impl_libsamplerate *u; - unsigned n_samples; - int i_skip, o_skip; - unsigned oc; - - assert(r); - assert(input); - assert(r->impl_data); - u = r->impl_data; - - /* Remap channels and place the result int buf2 */ - - if (!u->map_required) - return input; - - n_samples = n_frames * r->o_ss.channels; - - if (u->buf2_samples < n_samples) - u->buf2 = pa_xrealloc(u->buf2, sizeof(float) * (u->buf2_samples = n_samples)); - - memset(u->buf2, 0, n_samples * sizeof(float)); - - o_skip = sizeof(float) * r->o_ss.channels; - i_skip = sizeof(float) * r->i_ss.channels; - - for (oc = 0; oc < r->o_ss.channels; oc++) { - unsigned i; - static const float one = 1.0; - - for (i = 0; i < PA_CHANNELS_MAX && u->map_table[oc][i] >= 0; i++) - oil_vectoradd_f32( - u->buf2 + oc, o_skip, - u->buf2 + oc, o_skip, - input + u->map_table[oc][i], i_skip, - n_frames, - &one, &one); - } - - return u->buf2; -} - -static float *resample(pa_resampler *r, float *input, unsigned *n_frames) { - struct impl_libsamplerate *u; - SRC_DATA data; - unsigned out_n_frames, out_n_samples; - int ret; - - assert(r); - assert(input); - assert(n_frames); - assert(r->impl_data); - u = r->impl_data; - - /* Resample the data and place the result in buf3 */ - - if (!u->src_state) - return input; - - out_n_frames = (*n_frames*r->o_ss.rate/r->i_ss.rate)+1024; - out_n_samples = out_n_frames * r->o_ss.channels; - - if (u->buf3_samples < out_n_samples) - u->buf3 = pa_xrealloc(u->buf3, sizeof(float) * (u->buf3_samples = out_n_samples)); - - data.data_in = input; - data.input_frames = *n_frames; - - data.data_out = u->buf3; - data.output_frames = out_n_frames; - - data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate; - data.end_of_input = 0; - - ret = src_process(u->src_state, &data); - assert(ret == 0); - assert((unsigned) data.input_frames_used == *n_frames); - - *n_frames = data.output_frames_gen; - - return u->buf3; -} - -static float *convert_from_float(pa_resampler *r, float *input, unsigned n_frames) { - struct impl_libsamplerate *u; - unsigned n_samples; - - assert(r); - assert(input); - assert(r->impl_data); - u = r->impl_data; - - /* Convert the data into the correct sample type and place the result in buf4 */ - - if (!u->from_float32ne_func) - return input; - - n_samples = n_frames * r->o_ss.channels; - - if (u->buf4_samples < n_samples) - u->buf4 = pa_xrealloc(u->buf4, sizeof(float) * (u->buf4_samples = n_samples)); - - u->from_float32ne_func(n_samples, input, u->buf4); - - return u->buf4; -} - -static void libsamplerate_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) { - struct impl_libsamplerate *u; - float *buf, *input; - unsigned n_frames; - - assert(r); - assert(in); - assert(out); - assert(in->length); - assert(in->memblock); - assert(in->length % r->i_fz == 0); - assert(r->impl_data); - - u = r->impl_data; - - buf = input = (float*) ((uint8_t*) in->memblock->data + in->index); - n_frames = in->length / r->i_fz; - assert(n_frames > 0); - - buf = convert_to_float(r, buf, n_frames); - buf = remap_channels(r, buf, n_frames); - buf = resample(r, buf, &n_frames); - - if (n_frames) { - buf = convert_from_float(r, buf, n_frames); - - if (buf == input) { - /* Mm, no adjustment has been necessary, so let's return the original block */ - out->memblock = pa_memblock_ref(in->memblock); - out->index = in->index; - out->length = in->length; - } else { - float **p = NULL; - - out->length = n_frames * r->o_fz; - out->index = 0; - - if (buf == u->buf1) { - p = &u->buf1; - u->buf1_samples = 0; - } else if (buf == u->buf2) { - p = &u->buf2; - u->buf2_samples = 0; - } else if (buf == u->buf3) { - p = &u->buf3; - u->buf3_samples = 0; - } else if (buf == u->buf4) { - p = &u->buf4; - u->buf4_samples = 0; - } - - assert(p); - - /* Take the existing buffer and make it a memblock */ - out->memblock = pa_memblock_new_dynamic(*p, out->length, r->memblock_stat); - *p = NULL; - } - } else { - out->memblock = NULL; - out->index = out->length = 0; - } -} - -static void libsamplerate_update_input_rate(pa_resampler *r, uint32_t rate) { - struct impl_libsamplerate *u; - - assert(r); - assert(rate > 0); - assert(r->impl_data); - u = r->impl_data; - - if (!u->src_state) { - int err; - u->src_state = src_new(r->resample_method, r->o_ss.channels, &err); - assert(u->src_state); - } else { - int ret = src_set_ratio(u->src_state, (double) r->o_ss.rate / rate); - assert(ret == 0); - } -} - -static int libsamplerate_init(pa_resampler *r) { - struct impl_libsamplerate *u = NULL; - int err; - - r->impl_data = u = pa_xnew(struct impl_libsamplerate, 1); - - u->buf1 = u->buf2 = u->buf3 = u->buf4 = NULL; - u->buf1_samples = u->buf2_samples = u->buf3_samples = u->buf4_samples = 0; - - if (r->i_ss.format == PA_SAMPLE_FLOAT32NE) - u->to_float32ne_func = NULL; - else if (!(u->to_float32ne_func = pa_get_convert_to_float32ne_function(r->i_ss.format))) - goto fail; - - if (r->o_ss.format == PA_SAMPLE_FLOAT32NE) - u->from_float32ne_func = NULL; - else if (!(u->from_float32ne_func = pa_get_convert_from_float32ne_function(r->o_ss.format))) - goto fail; - - if (r->o_ss.rate == r->i_ss.rate) - u->src_state = NULL; - else if (!(u->src_state = src_new(r->resample_method, r->o_ss.channels, &err))) - goto fail; - - r->impl_free = libsamplerate_free; - r->impl_update_input_rate = libsamplerate_update_input_rate; - r->impl_run = libsamplerate_run; - - calc_map_table(r); - - return 0; - -fail: - pa_xfree(u); - return -1; -} - -/* Trivial implementation */ - -static void trivial_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) { - size_t fz; - unsigned n_frames; - struct impl_trivial *u; - - assert(r); - assert(in); - assert(out); - assert(r->impl_data); - - u = r->impl_data; - - fz = r->i_fz; - assert(fz == r->o_fz); - - n_frames = in->length/fz; - - if (r->i_ss.rate == r->o_ss.rate) { - - /* In case there's no diefference in sample types, do nothing */ - *out = *in; - pa_memblock_ref(out->memblock); - - u->o_counter += n_frames; - } else { - /* Do real resampling */ - size_t l; - unsigned o_index; - - /* The length of the new memory block rounded up */ - l = ((((n_frames+1) * r->o_ss.rate) / r->i_ss.rate) + 1) * fz; - - out->index = 0; - out->memblock = pa_memblock_new(l, r->memblock_stat); - - for (o_index = 0;; o_index++, u->o_counter++) { - unsigned j; - - j = (u->o_counter * r->i_ss.rate / r->o_ss.rate); - j = j > u->i_counter ? j - u->i_counter : 0; - - if (j >= n_frames) - break; - - assert(o_index*fz < out->memblock->length); - - memcpy((uint8_t*) out->memblock->data + fz*o_index, - (uint8_t*) in->memblock->data + in->index + fz*j, fz); - - } - - out->length = o_index*fz; - } - - u->i_counter += n_frames; - - /* Normalize counters */ - while (u->i_counter >= r->i_ss.rate) { - u->i_counter -= r->i_ss.rate; - assert(u->o_counter >= r->o_ss.rate); - u->o_counter -= r->o_ss.rate; - } -} - -static void trivial_free(pa_resampler *r) { - assert(r); - - pa_xfree(r->impl_data); -} - -static void trivial_update_input_rate(pa_resampler *r, uint32_t rate) { - struct impl_trivial *u; - - assert(r); - assert(rate > 0); - assert(r->impl_data); - - u = r->impl_data; - u->i_counter = 0; - u->o_counter = 0; -} - -static int trivial_init(pa_resampler*r) { - struct impl_trivial *u; - - assert(r); - assert(r->i_ss.format == r->o_ss.format); - assert(r->i_ss.channels == r->o_ss.channels); - - r->impl_data = u = pa_xnew(struct impl_trivial, 1); - u->o_counter = u->i_counter = 0; - - r->impl_run = trivial_run; - r->impl_free = trivial_free; - r->impl_update_input_rate = trivial_update_input_rate; - - return 0; -} - - diff --git a/polyp/resampler.h b/polyp/resampler.h deleted file mode 100644 index e14942f3..00000000 --- a/polyp/resampler.h +++ /dev/null @@ -1,73 +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 - -#include "sample.h" -#include "memblock.h" -#include "memchunk.h" -#include "channelmap.h" - -typedef struct pa_resampler pa_resampler; - -typedef enum pa_resample_method { - PA_RESAMPLER_INVALID = -1, - PA_RESAMPLER_SRC_SINC_BEST_QUALITY = SRC_SINC_BEST_QUALITY, - PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY = SRC_SINC_MEDIUM_QUALITY, - PA_RESAMPLER_SRC_SINC_FASTEST = SRC_SINC_FASTEST, - PA_RESAMPLER_SRC_ZERO_ORDER_HOLD = SRC_ZERO_ORDER_HOLD, - PA_RESAMPLER_SRC_LINEAR = SRC_LINEAR, - PA_RESAMPLER_TRIVIAL, - PA_RESAMPLER_MAX -} pa_resample_method_t; - -pa_resampler* pa_resampler_new( - const pa_sample_spec *a, - const pa_channel_map *am, - const pa_sample_spec *b, - const pa_channel_map *bm, - pa_memblock_stat *s, - pa_resample_method_t resample_method); - -void pa_resampler_free(pa_resampler *r); - -/* Returns the size of an input memory block which is required to return the specified amount of output data */ -size_t pa_resampler_request(pa_resampler *r, size_t out_length); - -/* Pass the specified memory chunk to the resampler and return the newly resampled data */ -void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out); - -/* Change the input rate of the resampler object */ -void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate); - -/* Return the resampling method of the resampler object */ -pa_resample_method_t pa_resampler_get_method(pa_resampler *r); - -/* Try to parse the resampler method */ -pa_resample_method_t pa_parse_resample_method(const char *string); - -/* return a human readable string for the specified resampling method. Inverse of pa_parse_resample_method() */ -const char *pa_resample_method_to_string(pa_resample_method_t m); - -#endif diff --git a/polyp/sample-util.c b/polyp/sample-util.c deleted file mode 100644 index 52974c46..00000000 --- a/polyp/sample-util.c +++ /dev/null @@ -1,328 +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 -#endif - -#include -#include -#include -#include - -#include - -#include "log.h" -#include "sample-util.h" - -pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) { - assert(b && b->data && spec); - pa_silence_memory(b->data, b->length, spec); - return b; -} - -void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) { - assert(c && c->memblock && c->memblock->data && spec && c->length); - - pa_silence_memory((uint8_t*) c->memblock->data+c->index, c->length, spec); -} - -void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) { - uint8_t c = 0; - assert(p && length && spec); - - switch (spec->format) { - case PA_SAMPLE_U8: - c = 0x80; - break; - case PA_SAMPLE_S16LE: - case PA_SAMPLE_S16BE: - case PA_SAMPLE_FLOAT32: - c = 0; - break; - case PA_SAMPLE_ALAW: - case PA_SAMPLE_ULAW: - c = 80; - break; - default: - assert(0); - } - - memset(p, c, length); -} - -size_t pa_mix( - const pa_mix_info streams[], - unsigned nstreams, - void *data, - size_t length, - const pa_sample_spec *spec, - const pa_cvolume *volume) { - - assert(streams && data && length && spec); - - switch (spec->format) { - case PA_SAMPLE_S16NE:{ - size_t d; - unsigned channel = 0; - - for (d = 0;; d += sizeof(int16_t)) { - int32_t sum = 0; - - if (d >= length) - return d; - - if (volume->values[channel] != PA_VOLUME_MUTED) { - unsigned i; - - for (i = 0; i < nstreams; i++) { - int32_t v; - pa_volume_t cvolume = streams[i].volume.values[channel]; - - if (d >= streams[i].chunk.length) - return d; - - if (cvolume == PA_VOLUME_MUTED) - v = 0; - else { - v = *((int16_t*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d)); - - if (cvolume != PA_VOLUME_NORM) { - v *= cvolume; - v /= PA_VOLUME_NORM; - } - } - - sum += v; - } - - if (volume->values[channel] != PA_VOLUME_NORM) { - sum *= volume->values[channel]; - sum /= PA_VOLUME_NORM; - } - - if (sum < -0x8000) sum = -0x8000; - if (sum > 0x7FFF) sum = 0x7FFF; - - } - - *((int16_t*) data) = sum; - data = (uint8_t*) data + sizeof(int16_t); - - if (++channel >= spec->channels) - channel = 0; - } - } - - case PA_SAMPLE_U8: { - size_t d; - unsigned channel = 0; - - for (d = 0;; d ++) { - int32_t sum = 0; - - if (d >= length) - return d; - - if (volume->values[channel] != PA_VOLUME_MUTED) { - unsigned i; - - for (i = 0; i < nstreams; i++) { - int32_t v; - pa_volume_t cvolume = streams[i].volume.values[channel]; - - if (d >= streams[i].chunk.length) - return d; - - if (cvolume == PA_VOLUME_MUTED) - v = 0; - else { - v = (int32_t) *((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d) - 0x80; - - if (cvolume != PA_VOLUME_NORM) { - v *= cvolume; - v /= PA_VOLUME_NORM; - } - } - - sum += v; - } - - if (volume->values[channel] != PA_VOLUME_NORM) { - sum *= volume->values[channel]; - sum /= PA_VOLUME_NORM; - } - - if (sum < -0x80) sum = -0x80; - if (sum > 0x7F) sum = 0x7F; - - } - - *((uint8_t*) data) = (uint8_t) (sum + 0x80); - data = (uint8_t*) data + 1; - - if (++channel >= spec->channels) - channel = 0; - } - } - - case PA_SAMPLE_FLOAT32NE: { - size_t d; - unsigned channel = 0; - - for (d = 0;; d += sizeof(float)) { - float sum = 0; - - if (d >= length) - return d; - - if (volume->values[channel] != PA_VOLUME_MUTED) { - unsigned i; - - for (i = 0; i < nstreams; i++) { - float v; - pa_volume_t cvolume = streams[i].volume.values[channel]; - - if (d >= streams[i].chunk.length) - return d; - - if (cvolume == PA_VOLUME_MUTED) - v = 0; - else { - v = *((float*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d)); - - if (cvolume != PA_VOLUME_NORM) { - v *= cvolume; - v /= PA_VOLUME_NORM; - } - } - - sum += v; - } - - if (volume->values[channel] != PA_VOLUME_NORM) { - sum *= volume->values[channel]; - sum /= PA_VOLUME_NORM; - } - } - - *((float*) data) = sum; - data = (uint8_t*) data + sizeof(float); - - if (++channel >= spec->channels) - channel = 0; - } - } - - default: - abort(); - } -} - - -void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvolume *volume) { - assert(c && spec && (c->length % pa_frame_size(spec) == 0)); - assert(volume); - - if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_NORM)) - return; - - if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_MUTED)) { - pa_silence_memchunk(c, spec); - return; - } - - switch (spec->format) { - case PA_SAMPLE_S16NE: { - int16_t *d; - size_t n; - unsigned channel = 0; - - for (d = (int16_t*) ((uint8_t*) c->memblock->data+c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) { - int32_t t = (int32_t)(*d); - - t *= volume->values[channel]; - t /= PA_VOLUME_NORM; - - if (t < -0x8000) t = -0x8000; - if (t > 0x7FFF) t = 0x7FFF; - - *d = (int16_t) t; - - if (++channel >= spec->channels) - channel = 0; - } - break; - } - - case PA_SAMPLE_U8: { - uint8_t *d; - size_t n; - unsigned channel = 0; - - for (d = (uint8_t*) c->memblock->data + c->index, n = c->length; n > 0; d++, n--) { - int32_t t = (int32_t) *d - 0x80; - - t *= volume->values[channel]; - t /= PA_VOLUME_NORM; - - if (t < -0x80) t = -0x80; - if (t > 0x7F) t = 0x7F; - - *d = (uint8_t) (t + 0x80); - - if (++channel >= spec->channels) - channel = 0; - } - break; - } - - case PA_SAMPLE_FLOAT32NE: { - float *d; - int skip; - unsigned n; - unsigned channel; - - d = (float*) ((uint8_t*) c->memblock->data + c->index); - skip = spec->channels * sizeof(float); - n = c->length/sizeof(float)/spec->channels; - - for (channel = 0; channel < spec->channels ; channel ++) { - float v, *t; - - if (volume->values[channel] == PA_VOLUME_NORM) - continue; - - v = (float) volume->values[channel] / PA_VOLUME_NORM; - - t = d + channel; - oil_scalarmult_f32(t, skip, t, skip, &v, n); - } - break; - } - - default: - pa_log_error(__FILE__": ERROR: Unable to change volume of format %s.\n", - pa_sample_format_to_string(spec->format)); - abort(); - } -} - diff --git a/polyp/sample-util.h b/polyp/sample-util.h deleted file mode 100644 index e433f9c8..00000000 --- a/polyp/sample-util.h +++ /dev/null @@ -1,53 +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" -#include "volume.h" - -pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec); -void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec); -void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec); - -typedef struct pa_mix_info { - pa_memchunk chunk; - pa_cvolume volume; - void *userdata; -} pa_mix_info; - -size_t pa_mix( - const pa_mix_info channels[], - unsigned nchannels, - void *data, - size_t length, - const pa_sample_spec *spec, - const pa_cvolume *volume); - -void pa_volume_memchunk( - pa_memchunk*c, - const pa_sample_spec *spec, - const pa_cvolume *volume); - -#endif diff --git a/polyp/sample.c b/polyp/sample.c deleted file mode 100644 index d587170c..00000000 --- a/polyp/sample.c +++ /dev/null @@ -1,149 +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 -#endif - -#include -#include -#include -#include - -#include "sample.h" - -size_t pa_sample_size(const pa_sample_spec *spec) { - assert(spec); - - switch (spec->format) { - case PA_SAMPLE_U8: - case PA_SAMPLE_ULAW: - case PA_SAMPLE_ALAW: - return 1; - case PA_SAMPLE_S16LE: - case PA_SAMPLE_S16BE: - return 2; - case PA_SAMPLE_FLOAT32LE: - case PA_SAMPLE_FLOAT32BE: - return 4; - default: - assert(0); - } -} - -size_t pa_frame_size(const pa_sample_spec *spec) { - assert(spec); - - return pa_sample_size(spec) * spec->channels; -} - -size_t pa_bytes_per_second(const pa_sample_spec *spec) { - assert(spec); - return spec->rate*pa_frame_size(spec); -} - -pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) { - assert(spec); - - return (pa_usec_t) (((double) length/pa_frame_size(spec)*1000000)/spec->rate); -} - -int pa_sample_spec_valid(const pa_sample_spec *spec) { - assert(spec); - - if (spec->rate <= 0 || - spec->channels <= 0 || - spec->channels > PA_CHANNELS_MAX || - spec->format >= PA_SAMPLE_MAX || - spec->format < 0) - return 0; - - return 1; -} - -int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) { - assert(a && b); - - return (a->format == b->format) && (a->rate == b->rate) && (a->channels == b->channels); -} - -const char *pa_sample_format_to_string(pa_sample_format_t f) { - static const char* const table[]= { - [PA_SAMPLE_U8] = "u8", - [PA_SAMPLE_ALAW] = "aLaw", - [PA_SAMPLE_ULAW] = "uLaw", - [PA_SAMPLE_S16LE] = "s16le", - [PA_SAMPLE_S16BE] = "s16be", - [PA_SAMPLE_FLOAT32LE] = "float32le", - [PA_SAMPLE_FLOAT32BE] = "float32be", - }; - - if (f >= PA_SAMPLE_MAX) - return NULL; - - return table[f]; -} - -char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) { - assert(s && l && spec); - - if (!pa_sample_spec_valid(spec)) - snprintf(s, l, "Invalid"); - else - snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate); - - return s; -} - -void pa_bytes_snprint(char *s, size_t l, unsigned v) { - if (v >= ((unsigned) 1024)*1024*1024) - snprintf(s, l, "%0.1f GB", ((double) v)/1024/1024/1024); - else if (v >= ((unsigned) 1024)*1024) - snprintf(s, l, "%0.1f MB", ((double) v)/1024/1024); - else if (v >= (unsigned) 1024) - snprintf(s, l, "%0.1f KB", ((double) v)/1024); - else - snprintf(s, l, "%u B", (unsigned) v); -} - -pa_sample_format_t pa_parse_sample_format(const char *format) { - - if (strcasecmp(format, "s16le") == 0) - return PA_SAMPLE_S16LE; - else if (strcasecmp(format, "s16be") == 0) - return PA_SAMPLE_S16BE; - else if (strcasecmp(format, "s16ne") == 0 || strcasecmp(format, "s16") == 0 || strcasecmp(format, "16") == 0) - return PA_SAMPLE_S16NE; - else if (strcasecmp(format, "u8") == 0 || strcasecmp(format, "8") == 0) - return PA_SAMPLE_U8; - else if (strcasecmp(format, "float32") == 0 || strcasecmp(format, "float32ne") == 0) - return PA_SAMPLE_FLOAT32; - else if (strcasecmp(format, "float32le") == 0) - return PA_SAMPLE_FLOAT32LE; - else if (strcasecmp(format, "float32be") == 0) - return PA_SAMPLE_FLOAT32BE; - else if (strcasecmp(format, "ulaw") == 0) - return PA_SAMPLE_ULAW; - else if (strcasecmp(format, "alaw") == 0) - return PA_SAMPLE_ALAW; - - return -1; -} diff --git a/polyp/sample.h b/polyp/sample.h deleted file mode 100644 index c1b98f1c..00000000 --- a/polyp/sample.h +++ /dev/null @@ -1,120 +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 -#include -#include - -#include - -/** \file - * Constants and routines for sample type handling */ - -PA_C_DECL_BEGIN - -/* Maximum allowed channels */ -#define PA_CHANNELS_MAX 16 - -/** Sample format */ -typedef enum pa_sample_format { - PA_SAMPLE_U8, /**< Unsigned 8 Bit PCM */ - PA_SAMPLE_ALAW, /**< 8 Bit a-Law */ - PA_SAMPLE_ULAW, /**< 8 Bit mu-Law */ - PA_SAMPLE_S16LE, /**< Signed 16 Bit PCM, little endian (PC) */ - PA_SAMPLE_S16BE, /**< Signed 16 Bit PCM, big endian */ - PA_SAMPLE_FLOAT32LE, /**< 32 Bit IEEE floating point, little endian, range -1..1 */ - PA_SAMPLE_FLOAT32BE, /**< 32 Bit IEEE floating point, big endian, range -1..1 */ - PA_SAMPLE_MAX, /**< Upper limit of valid sample types */ - PA_SAMPLE_INVALID = -1 /**< An invalid value */ -} pa_sample_format_t; - -#ifdef WORDS_BIGENDIAN -/** Signed 16 Bit PCM, native endian */ -#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE -/** 32 Bit IEEE floating point, native endian */ -#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE -/** Signed 16 Bit PCM reverse endian */ -#define PA_SAMPLE_S16RE PA_SAMPLE_S16LE -/** 32 Bit IEEE floating point, reverse endian */ -#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32LE -#else -/** Signed 16 Bit PCM, native endian */ -#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE -/** 32 Bit IEEE floating point, native endian */ -#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE -/** Signed 16 Bit PCM reverse endian */ -#define PA_SAMPLE_S16RE PA_SAMPLE_S16BE -/** 32 Bit IEEE floating point, reverse endian */ -#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32BE -#endif - -/** A Shortcut for PA_SAMPLE_FLOAT32NE */ -#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32NE - -/** A sample format and attribute specification */ -typedef struct pa_sample_spec { - pa_sample_format_t format; /**< The sample format */ - uint32_t rate; /**< The sample rate. (e.g. 44100) */ - uint8_t channels; /**< Audio channels. (1 for mono, 2 for stereo, ...) */ -} pa_sample_spec; - -/** Type for usec specifications (unsigned). May be either 32 or 64 bit, depending on the architecture */ -typedef uint64_t pa_usec_t; - -/** Return the amount of bytes playback of a second of audio with the specified sample type takes */ -size_t pa_bytes_per_second(const pa_sample_spec *spec); - -/** Return the size of a frame with the specific sample type */ -size_t pa_frame_size(const pa_sample_spec *spec); - -/** Return the size of a sample with the specific sample type */ -size_t pa_sample_size(const pa_sample_spec *spec); - -/** Calculate the time the specified bytes take to play with the specified sample type */ -pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec); - -/** Return non-zero when the sample type specification is valid */ -int pa_sample_spec_valid(const pa_sample_spec *spec); - -/** Return non-zero when the two sample type specifications match */ -int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b); - -/* Return a descriptive string for the specified sample format. \since 0.8 */ -const char *pa_sample_format_to_string(pa_sample_format_t f); - -/** Parse a sample format text. Inverse of pa_sample_format_to_string() */ -pa_sample_format_t pa_parse_sample_format(const char *format); - -/** Maximum required string length for pa_sample_spec_snprint() */ -#define PA_SAMPLE_SPEC_SNPRINT_MAX 32 - -/** Pretty print a sample type specification to a string */ -char* pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec); - -/** Pretty print a byte size value. (i.e. "2.5 MB") */ -void pa_bytes_snprint(char *s, size_t l, unsigned v); - -PA_C_DECL_END - -#endif diff --git a/polyp/scache.c b/polyp/scache.c deleted file mode 100644 index 39fa26f3..00000000 --- a/polyp/scache.c +++ /dev/null @@ -1,392 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_GLOB_H -#include -#endif - -#ifdef HAVE_WINDOWS_H -#include -#endif - -#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" -#include "channelmap.h" -#include "volume.h" - -#define UNLOAD_POLL_TIME 2 - -static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { - pa_core *c = userdata; - struct timeval ntv; - assert(c && c->mainloop == m && c->scache_auto_unload_event == e); - - pa_scache_unload_unused(c); - - pa_gettimeofday(&ntv); - ntv.tv_sec += UNLOAD_POLL_TIME; - m->time_restart(e, &ntv); -} - -static void free_entry(pa_scache_entry *e) { - assert(e); - pa_namereg_unregister(e->core, e->name); - pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_REMOVE, e->index); - pa_xfree(e->name); - pa_xfree(e->filename); - if (e->memchunk.memblock) - pa_memblock_unref(e->memchunk.memblock); - pa_xfree(e); -} - -static pa_scache_entry* scache_add_item(pa_core *c, const char *name) { - pa_scache_entry *e; - assert(c && name); - - if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) { - if (e->memchunk.memblock) - pa_memblock_unref(e->memchunk.memblock); - - pa_xfree(e->filename); - - assert(e->core == c); - - pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); - } else { - e = pa_xmalloc(sizeof(pa_scache_entry)); - - if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, 1)) { - pa_xfree(e); - return NULL; - } - - e->name = pa_xstrdup(name); - e->core = c; - - if (!c->scache) { - c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - assert(c->scache); - } - - pa_idxset_put(c->scache, e, &e->index); - - pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_NEW, e->index); - } - - pa_cvolume_reset(&e->volume, PA_CHANNELS_MAX); - e->last_used_time = 0; - e->memchunk.memblock = NULL; - e->memchunk.index = e->memchunk.length = 0; - e->filename = NULL; - e->lazy = 0; - e->last_used_time = 0; - - memset(&e->sample_spec, 0, sizeof(pa_sample_spec)); - - return e; -} - -int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx) { - pa_scache_entry *e; - assert(c && name); - - if (!(e = scache_add_item(c, name))) - return -1; - - if (ss) { - e->sample_spec = *ss; - pa_channel_map_init_auto(&e->channel_map, ss->channels); - } - - if (map) - e->channel_map = *map; - - if (chunk) { - e->memchunk = *chunk; - pa_memblock_ref(e->memchunk.memblock); - } - - if (idx) - *idx = e->index; - - return 0; -} - -int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx) { - pa_sample_spec ss; - pa_memchunk chunk; - int r; - -#ifdef OS_IS_WIN32 - char buf[MAX_PATH]; - - if (ExpandEnvironmentStrings(filename, buf, MAX_PATH)) - filename = buf; -#endif - - if (pa_sound_file_load(filename, &ss, &chunk, c->memblock_stat) < 0) - return -1; - - r = pa_scache_add_item(c, name, &ss, NULL, &chunk, idx); - pa_memblock_unref(chunk.memblock); - - return r; -} - -int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx) { - pa_scache_entry *e; - -#ifdef OS_IS_WIN32 - char buf[MAX_PATH]; - - if (ExpandEnvironmentStrings(filename, buf, MAX_PATH)) - filename = buf; -#endif - - assert(c && name); - - if (!(e = scache_add_item(c, name))) - return -1; - - e->lazy = 1; - e->filename = pa_xstrdup(filename); - - if (!c->scache_auto_unload_event) { - struct timeval ntv; - pa_gettimeofday(&ntv); - ntv.tv_sec += UNLOAD_POLL_TIME; - c->scache_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c); - } - - if (idx) - *idx = e->index; - - return 0; -} - -int pa_scache_remove_item(pa_core *c, const char *name) { - pa_scache_entry *e; - assert(c && name); - - if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) - return -1; - - if (pa_idxset_remove_by_data(c->scache, e, NULL) != e) - assert(0); - - free_entry(e); - return 0; -} - -static void free_cb(void *p, PA_GCC_UNUSED void *userdata) { - pa_scache_entry *e = p; - assert(e); - free_entry(e); -} - -void pa_scache_free(pa_core *c) { - assert(c); - - if (c->scache) { - pa_idxset_free(c->scache, free_cb, NULL); - c->scache = NULL; - } - - if (c->scache_auto_unload_event) - c->mainloop->time_free(c->scache_auto_unload_event); -} - -int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, const pa_cvolume *volume) { - pa_scache_entry *e; - char *t; - pa_cvolume r; - assert(c && name && sink); - - if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 1))) - return -1; - - if (e->lazy && !e->memchunk.memblock) { - if (pa_sound_file_load(e->filename, &e->sample_spec, &e->memchunk, c->memblock_stat) < 0) - return -1; - - pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); - } - - if (!e->memchunk.memblock) - return -1; - - t = pa_sprintf_malloc("sample:%s", name); - - if (pa_play_memchunk(sink, t, &e->sample_spec, &e->channel_map, &e->memchunk, pa_sw_cvolume_multiply(&r, volume, &e->volume)) < 0) { - free(t); - return -1; - } - - free(t); - - if (e->lazy) - time(&e->last_used_time); - - return 0; -} - -const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) { - pa_scache_entry *e; - assert(c && id != PA_IDXSET_INVALID); - - if (!c->scache || !(e = pa_idxset_get_by_index(c->scache, id))) - return NULL; - - return e->name; -} - -uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) { - pa_scache_entry *e; - assert(c && name); - - if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) - return PA_IDXSET_INVALID; - - return e->index; -} - -uint32_t pa_scache_total_size(pa_core *c) { - pa_scache_entry *e; - uint32_t idx, sum = 0; - assert(c); - - if (!c->scache || !pa_idxset_size(c->scache)) - return 0; - - for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) - if (e->memchunk.memblock) - sum += e->memchunk.length; - - return sum; -} - -void pa_scache_unload_unused(pa_core *c) { - pa_scache_entry *e; - time_t now; - uint32_t idx; - assert(c); - - if (!c->scache || !pa_idxset_size(c->scache)) - return; - - time(&now); - - for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) { - - if (!e->lazy || !e->memchunk.memblock) - continue; - - if (e->last_used_time + c->scache_idle_time > now) - continue; - - pa_memblock_unref(e->memchunk.memblock); - e->memchunk.memblock = NULL; - e->memchunk.index = e->memchunk.length = 0; - - pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); - } -} - -static void add_file(pa_core *c, const char *pathname) { - struct stat st; - const char *e; - - e = pa_path_get_filename(pathname); - - if (stat(pathname, &st) < 0) { - pa_log(__FILE__": stat('%s') failed: %s\n", pathname, strerror(errno)); - return; - } - -#if defined(S_ISREG) && defined(S_ISLNK) - if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) -#endif - pa_scache_add_file_lazy(c, e, pathname, NULL); -} - -int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) { - DIR *dir; - assert(c && pathname); - - /* First try to open this as directory */ - if (!(dir = opendir(pathname))) { -#ifdef HAVE_GLOB_H - glob_t p; - unsigned int i; - /* If that fails, try to open it as shell glob */ - - if (glob(pathname, GLOB_ERR|GLOB_NOSORT, NULL, &p) < 0) { - pa_log(__FILE__": Failed to open directory: %s\n", strerror(errno)); - return -1; - } - - for (i = 0; i < p.gl_pathc; i++) - add_file(c, p.gl_pathv[i]); - - globfree(&p); -#else - return -1; -#endif - } else { - struct dirent *e; - - while ((e = readdir(dir))) { - char p[PATH_MAX]; - - if (e->d_name[0] == '.') - continue; - - snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name); - add_file(c, p); - } - } - - closedir(dir); - return 0; -} diff --git a/polyp/scache.h b/polyp/scache.h deleted file mode 100644 index d667ae60..00000000 --- a/polyp/scache.h +++ /dev/null @@ -1,62 +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" - -typedef struct pa_scache_entry { - pa_core *core; - uint32_t index; - char *name; - - pa_cvolume volume; - pa_sample_spec sample_spec; - pa_channel_map channel_map; - pa_memchunk memchunk; - - char *filename; - - int lazy; - time_t last_used_time; -} pa_scache_entry; - -int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx); -int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx); -int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx); - -int pa_scache_add_directory_lazy(pa_core *c, const char *pathname); - -int pa_scache_remove_item(pa_core *c, const char *name); -int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, const pa_cvolume *cvolume); -void pa_scache_free(pa_core *c); - -const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id); -uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name); - -uint32_t pa_scache_total_size(pa_core *c); - -void pa_scache_unload_unused(pa_core *c); - -#endif diff --git a/polyp/sconv-s16be.c b/polyp/sconv-s16be.c deleted file mode 100644 index 8b076f06..00000000 --- a/polyp/sconv-s16be.c +++ /dev/null @@ -1,41 +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 -#endif - -#include "endianmacros.h" - -#define INT16_FROM INT16_FROM_BE -#define INT16_TO INT16_TO_BE - -#define pa_sconv_s16le_to_float32ne pa_sconv_s16be_to_float32ne -#define pa_sconv_s16le_from_float32ne pa_sconv_s16be_from_float32ne - -#ifdef WORDS_BIGENDIAN -#define SWAP_WORDS 0 -#else -#define SWAP_WORDS 1 -#endif - -#include "sconv-s16le.h" -#include "sconv-s16le.c" diff --git a/polyp/sconv-s16be.h b/polyp/sconv-s16be.h deleted file mode 100644 index b2b6a8c7..00000000 --- a/polyp/sconv-s16be.h +++ /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, float *b); -void pa_sconv_s16be_from_float32ne(unsigned n, const float *a, void *b); - -#endif diff --git a/polyp/sconv-s16le.c b/polyp/sconv-s16le.c deleted file mode 100644 index e47c5e8e..00000000 --- a/polyp/sconv-s16le.c +++ /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 -#endif - -#include -#include - -#include - -#include "endianmacros.h" -#include "sconv.h" -#include "sconv-s16le.h" -#include "log.h" - -#ifndef INT16_FROM -#define INT16_FROM INT16_FROM_LE -#endif - -#ifndef INT16_TO -#define INT16_TO INT16_TO_LE -#endif - -#ifndef SWAP_WORDS -#ifdef WORDS_BIGENDIAN -#define SWAP_WORDS 1 -#else -#define SWAP_WORDS 0 -#endif -#endif - -void pa_sconv_s16le_to_float32ne(unsigned n, const void *a, float *b) { - const int16_t *ca = a; - - assert(a); - assert(b); - -#if SWAP_WORDS == 1 - - for (; n > 0; n--) { - int16_t s = *(ca++); - *(b++) = ((float) INT16_FROM(s))/0x7FFF; - } - -#else -{ - static const double add = 0, factor = 1.0/0x7FFF; - oil_scaleconv_f32_s16(b, ca, n, &add, &factor); -} -#endif -} - -void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, void *b) { - int16_t *cb = b; - - assert(a); - assert(b); - -#if SWAP_WORDS == 1 - - for (; n > 0; n--) { - int16_t s; - float v = *(a++); - - if (v > 1) - v = 1; - - if (v < -1) - v = -1; - - s = (int16_t) (v * 0x7FFF); - *(cb++) = INT16_TO(s); - } - -#else -{ - static const double add = 0, factor = 0x7FFF; - oil_scaleconv_s16_f32(cb, a, n, &add, &factor); -} -#endif -} diff --git a/polyp/sconv-s16le.h b/polyp/sconv-s16le.h deleted file mode 100644 index ef5e31e8..00000000 --- a/polyp/sconv-s16le.h +++ /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, float *b); -void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, void *b); - -#endif diff --git a/polyp/sconv.c b/polyp/sconv.c deleted file mode 100644 index 1fcb5a0c..00000000 --- a/polyp/sconv.c +++ /dev/null @@ -1,168 +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 -#endif - -#include -#include -#include - -#include -#include - -#include "endianmacros.h" -#include "sconv.h" -#include "g711.h" - -#include "sconv-s16le.h" -#include "sconv-s16be.h" - -static void u8_to_float32ne(unsigned n, const void *a, float *b) { - const uint8_t *ca = a; - static const double add = -128.0/127.0, factor = 1.0/127.0; - - assert(a); - assert(b); - - oil_scaleconv_f32_u8(b, ca, n, &add, &factor); -} - -static void u8_from_float32ne(unsigned n, const float *a, void *b) { - uint8_t *cb = b; - static const double add = 128.0, factor = 127.0; - - assert(a); - assert(b); - - oil_scaleconv_u8_f32(cb, a, n, &add, &factor); -} - -static void float32ne_to_float32ne(unsigned n, const void *a, float *b) { - assert(a); - assert(b); - - oil_memcpy(b, a, sizeof(float) * n); -} - -static void float32ne_from_float32ne(unsigned n, const float *a, void *b) { - assert(a); - assert(b); - - oil_memcpy(b, a, sizeof(float) * n); -} - -static void ulaw_to_float32ne(unsigned n, const void *a, float *b) { - const uint8_t *ca = a; - - assert(a); - assert(b); - - for (; n > 0; n--) - *(b++) = st_ulaw2linear16(*(ca++)) * 1.0F / 0x7FFF; -} - -static void ulaw_from_float32ne(unsigned n, const float *a, void *b) { - uint8_t *cb = b; - - assert(a); - assert(b); - - for (; n > 0; n--) { - float v = *(a++); - - if (v > 1) - v = 1; - - if (v < -1) - v = -1; - - *(cb++) = st_14linear2ulaw((int16_t) (v * 0x1FFF)); - } -} - -static void alaw_to_float32ne(unsigned n, const void *a, float *b) { - const uint8_t *ca = a; - - assert(a); - assert(b); - - for (; n > 0; n--) - *(b++) = st_alaw2linear16(*(ca++)) * 1.0F / 0x7FFF; -} - -static void alaw_from_float32ne(unsigned n, const float *a, void *b) { - uint8_t *cb = b; - - assert(a); - assert(b); - - for (; n > 0; n--) { - float v = *(a++); - - if (v > 1) - v = 1; - - if (v < -1) - v = -1; - - *(cb++) = st_13linear2alaw((int16_t) (v * 0xFFF)); - } -} - -pa_convert_to_float32ne_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) { - switch(f) { - case PA_SAMPLE_U8: - return u8_to_float32ne; - case PA_SAMPLE_S16LE: - return pa_sconv_s16le_to_float32ne; - case PA_SAMPLE_S16BE: - return pa_sconv_s16be_to_float32ne; - case PA_SAMPLE_FLOAT32NE: - return float32ne_to_float32ne; - case PA_SAMPLE_ALAW: - return alaw_to_float32ne; - case PA_SAMPLE_ULAW: - return ulaw_to_float32ne; - default: - return NULL; - } -} - -pa_convert_from_float32ne_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) { - switch(f) { - case PA_SAMPLE_U8: - return u8_from_float32ne; - case PA_SAMPLE_S16LE: - return pa_sconv_s16le_from_float32ne; - case PA_SAMPLE_S16BE: - return pa_sconv_s16be_from_float32ne; - case PA_SAMPLE_FLOAT32NE: - return float32ne_from_float32ne; - case PA_SAMPLE_ALAW: - return alaw_from_float32ne; - case PA_SAMPLE_ULAW: - return ulaw_from_float32ne; - default: - return NULL; - } -} diff --git a/polyp/sconv.h b/polyp/sconv.h deleted file mode 100644 index 2a005219..00000000 --- a/polyp/sconv.h +++ /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, float *b); -typedef void (*pa_convert_from_float32ne_func_t)(unsigned n, const float *a, void *b); - -pa_convert_to_float32ne_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f); -pa_convert_from_float32ne_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f); - -#endif diff --git a/polyp/sink-input.c b/polyp/sink-input.c deleted file mode 100644 index f447b8cf..00000000 --- a/polyp/sink-input.c +++ /dev/null @@ -1,368 +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 -#endif - -#include -#include -#include -#include - -#include "sink-input.h" -#include "sample-util.h" -#include "xmalloc.h" -#include "subscribe.h" -#include "log.h" - -#define CONVERT_BUFFER_LENGTH 4096 - -pa_sink_input* pa_sink_input_new( - pa_sink *s, - const char *driver, - const char *name, - const pa_sample_spec *spec, - const pa_channel_map *map, - int variable_rate, - int resample_method) { - - pa_sink_input *i; - pa_resampler *resampler = NULL; - int r; - char st[256]; - pa_channel_map tmap; - - assert(s); - assert(spec); - assert(s->state == PA_SINK_RUNNING); - - if (pa_idxset_size(s->inputs) >= PA_MAX_INPUTS_PER_SINK) { - pa_log_warn(__FILE__": Failed to create sink input: too many inputs per sink.\n"); - return NULL; - } - - if (resample_method == PA_RESAMPLER_INVALID) - resample_method = s->core->resample_method; - - if (!map) { - pa_channel_map_init_auto(&tmap, spec->channels); - map = &tmap; - } - - if (variable_rate || !pa_sample_spec_equal(spec, &s->sample_spec) || !pa_channel_map_equal(map, &s->channel_map)) - if (!(resampler = pa_resampler_new(spec, map, &s->sample_spec, &s->channel_map, s->core->memblock_stat, resample_method))) - return NULL; - - i = pa_xnew(pa_sink_input, 1); - i->ref = 1; - i->state = PA_SINK_INPUT_RUNNING; - i->name = pa_xstrdup(name); - i->driver = pa_xstrdup(driver); - i->owner = NULL; - i->sink = s; - i->client = NULL; - - i->sample_spec = *spec; - i->channel_map = *map; - - pa_cvolume_reset(&i->volume, spec->channels); - - i->peek = NULL; - i->drop = NULL; - i->kill = NULL; - i->get_latency = NULL; - i->underrun = NULL; - i->userdata = NULL; - - i->playing = 0; - - pa_memchunk_reset(&i->resampled_chunk); - i->resampler = resampler; - - assert(s->core); - r = pa_idxset_put(s->core->sink_inputs, i, &i->index); - assert(r == 0 && i->index != PA_IDXSET_INVALID); - r = pa_idxset_put(s->inputs, i, NULL); - assert(r == 0); - - pa_sample_spec_snprint(st, sizeof(st), spec); - pa_log_info(__FILE__": created %u \"%s\" on %u with sample spec \"%s\"\n", i->index, i->name, s->index, st); - - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index); - - return i; -} - -void pa_sink_input_disconnect(pa_sink_input *i) { - assert(i); - assert(i->state != PA_SINK_INPUT_DISCONNECTED); - assert(i->sink); - assert(i->sink->core); - - pa_idxset_remove_by_data(i->sink->core->sink_inputs, i, NULL); - pa_idxset_remove_by_data(i->sink->inputs, i, NULL); - - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index); - i->sink = NULL; - - i->peek = NULL; - i->drop = NULL; - i->kill = NULL; - i->get_latency = NULL; - i->underrun = NULL; - - i->playing = 0; - i->state = PA_SINK_INPUT_DISCONNECTED; -} - -static void sink_input_free(pa_sink_input* i) { - assert(i); - - if (i->state != PA_SINK_INPUT_DISCONNECTED) - pa_sink_input_disconnect(i); - - pa_log_info(__FILE__": freed %u \"%s\"\n", i->index, i->name); - - if (i->resampled_chunk.memblock) - pa_memblock_unref(i->resampled_chunk.memblock); - - if (i->resampler) - pa_resampler_free(i->resampler); - - pa_xfree(i->name); - pa_xfree(i->driver); - pa_xfree(i); -} - -void pa_sink_input_unref(pa_sink_input *i) { - assert(i); - assert(i->ref >= 1); - - if (!(--i->ref)) - sink_input_free(i); -} - -pa_sink_input* pa_sink_input_ref(pa_sink_input *i) { - assert(i); - assert(i->ref >= 1); - - i->ref++; - return i; -} - -void pa_sink_input_kill(pa_sink_input*i) { - assert(i); - assert(i->ref >= 1); - - if (i->kill) - i->kill(i); -} - -pa_usec_t pa_sink_input_get_latency(pa_sink_input *i) { - pa_usec_t r = 0; - - assert(i); - assert(i->ref >= 1); - - if (i->get_latency) - r += i->get_latency(i); - - if (i->resampled_chunk.memblock) - r += pa_bytes_to_usec(i->resampled_chunk.length, &i->sample_spec); - - return r; -} - -int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume) { - int ret = -1; - int do_volume_adj_here; - - assert(i); - assert(i->ref >= 1); - assert(chunk); - assert(volume); - - pa_sink_input_ref(i); - - if (!i->peek || !i->drop || i->state == PA_SINK_INPUT_CORKED) - goto finish; - - if (!i->resampler) { - do_volume_adj_here = 0; - ret = i->peek(i, chunk); - goto finish; - } - - do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map); - - while (!i->resampled_chunk.memblock) { - pa_memchunk tchunk; - size_t l; - - if ((ret = i->peek(i, &tchunk)) < 0) - goto finish; - - assert(tchunk.length); - - l = pa_resampler_request(i->resampler, CONVERT_BUFFER_LENGTH); - - if (l > tchunk.length) - l = tchunk.length; - - i->drop(i, &tchunk, l); - tchunk.length = l; - - /* It might be necessary to adjust the volume here */ - if (do_volume_adj_here) { - pa_memchunk_make_writable(&tchunk, i->sink->core->memblock_stat, 0); - pa_volume_memchunk(&tchunk, &i->sample_spec, &i->volume); - } - - pa_resampler_run(i->resampler, &tchunk, &i->resampled_chunk); - pa_memblock_unref(tchunk.memblock); - } - - assert(i->resampled_chunk.memblock); - assert(i->resampled_chunk.length); - - *chunk = i->resampled_chunk; - pa_memblock_ref(i->resampled_chunk.memblock); - - ret = 0; - -finish: - - if (ret < 0 && i->playing && i->underrun) - i->underrun(i); - - i->playing = ret >= 0; - - if (ret >= 0) { - /* Let's see if we had to apply the volume adjustment - * ourselves, or if this can be done by the sink for us */ - - if (do_volume_adj_here) - /* We've both the same channel map, so let's have the sink do the adjustment for us*/ - - pa_cvolume_reset(volume, i->sample_spec.channels); - else - /* We had different channel maps, so we already did the adjustment */ - *volume = i->volume; - } - - pa_sink_input_unref(i); - - return ret; -} - -void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { - assert(i); - assert(i->ref >= 1); - assert(length > 0); - - if (!i->resampler) { - if (i->drop) - i->drop(i, chunk, length); - return; - } - - assert(i->resampled_chunk.memblock); - assert(i->resampled_chunk.length >= length); - - i->resampled_chunk.index += length; - i->resampled_chunk.length -= length; - - if (i->resampled_chunk.length <= 0) { - pa_memblock_unref(i->resampled_chunk.memblock); - i->resampled_chunk.memblock = NULL; - i->resampled_chunk.index = i->resampled_chunk.length = 0; - } -} - -void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { - assert(i); - assert(i->ref >= 1); - assert(i->sink); - assert(i->sink->core); - - if (pa_cvolume_equal(&i->volume, volume)) - return; - - i->volume = *volume; - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); -} - -const pa_cvolume * pa_sink_input_get_volume(pa_sink_input *i) { - assert(i); - assert(i->ref >= 1); - - return &i->volume; -} - -void pa_sink_input_cork(pa_sink_input *i, int b) { - int n; - - assert(i); - assert(i->ref >= 1); - - if (i->state == PA_SINK_INPUT_DISCONNECTED) - return; - - n = i->state == PA_SINK_INPUT_CORKED && !b; - - i->state = b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING; - - if (n) - pa_sink_notify(i->sink); -} - -void pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) { - assert(i); - assert(i->resampler); - assert(i->ref >= 1); - - if (i->sample_spec.rate == rate) - return; - - i->sample_spec.rate = rate; - pa_resampler_set_input_rate(i->resampler, rate); -} - -void pa_sink_input_set_name(pa_sink_input *i, const char *name) { - assert(i); - assert(i->ref >= 1); - - pa_xfree(i->name); - i->name = pa_xstrdup(name); - - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); -} - -pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) { - assert(i); - assert(i->ref >= 1); - - if (!i->resampler) - return PA_RESAMPLER_INVALID; - - return pa_resampler_get_method(i->resampler); -} diff --git a/polyp/sink-input.h b/polyp/sink-input.h deleted file mode 100644 index 1db993f5..00000000 --- a/polyp/sink-input.h +++ /dev/null @@ -1,106 +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 - -typedef struct pa_sink_input pa_sink_input; - -#include "sink.h" -#include "sample.h" -#include "memblockq.h" -#include "resampler.h" -#include "module.h" -#include "client.h" - -typedef enum pa_sink_input_state { - PA_SINK_INPUT_RUNNING, - PA_SINK_INPUT_CORKED, - PA_SINK_INPUT_DISCONNECTED -} pa_sink_input_state_t; - -struct pa_sink_input { - int ref; - uint32_t index; - pa_sink_input_state_t state; - - char *name, *driver; - pa_module *owner; - - pa_sink *sink; - pa_client *client; - - pa_sample_spec sample_spec; - pa_channel_map channel_map; - - pa_cvolume volume; - - int (*peek) (pa_sink_input *i, pa_memchunk *chunk); - void (*drop) (pa_sink_input *i, const pa_memchunk *chunk, size_t length); - void (*kill) (pa_sink_input *i); - pa_usec_t (*get_latency) (pa_sink_input *i); - void (*underrun) (pa_sink_input *i); - - void *userdata; - - int playing; - - pa_memchunk resampled_chunk; - pa_resampler *resampler; -}; - -pa_sink_input* pa_sink_input_new( - pa_sink *s, - const char *driver, - const char *name, - const pa_sample_spec *spec, - const pa_channel_map *map, - int variable_rate, - int resample_method); - -void pa_sink_input_unref(pa_sink_input* i); -pa_sink_input* pa_sink_input_ref(pa_sink_input* i); - -/* To be called by the implementing module only */ -void pa_sink_input_disconnect(pa_sink_input* i); - -/* External code may request disconnection with this funcion */ -void pa_sink_input_kill(pa_sink_input*i); - -pa_usec_t pa_sink_input_get_latency(pa_sink_input *i); - -int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume); -void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length); - -void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume); -const pa_cvolume * pa_sink_input_get_volume(pa_sink_input *i); - -void pa_sink_input_cork(pa_sink_input *i, int b); - -void pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate); - -void pa_sink_input_set_name(pa_sink_input *i, const char *name); - -pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i); - -#endif diff --git a/polyp/sink.c b/polyp/sink.c deleted file mode 100644 index bb656649..00000000 --- a/polyp/sink.c +++ /dev/null @@ -1,445 +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 -#endif - -#include -#include -#include -#include - -#include "sink.h" -#include "sink-input.h" -#include "namereg.h" -#include "util.h" -#include "sample-util.h" -#include "xmalloc.h" -#include "subscribe.h" -#include "log.h" -#include "polyplib-introspect.h" - -#define MAX_MIX_CHANNELS 32 - -pa_sink* pa_sink_new( - pa_core *core, - const char *driver, - const char *name, - int fail, - const pa_sample_spec *spec, - const pa_channel_map *map) { - - pa_sink *s; - char *n = NULL; - char st[256]; - int r; - - assert(core); - assert(name); - assert(*name); - assert(spec); - - s = pa_xnew(pa_sink, 1); - - if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) { - pa_xfree(s); - return NULL; - } - - s->ref = 1; - s->core = core; - s->state = PA_SINK_RUNNING; - s->name = pa_xstrdup(name); - s->description = NULL; - s->driver = pa_xstrdup(driver); - s->owner = NULL; - - s->sample_spec = *spec; - if (map) - s->channel_map = *map; - else - pa_channel_map_init_auto(&s->channel_map, spec->channels); - - s->inputs = pa_idxset_new(NULL, NULL); - - pa_cvolume_reset(&s->sw_volume, spec->channels); - pa_cvolume_reset(&s->hw_volume, spec->channels); - - s->get_latency = NULL; - s->notify = NULL; - s->set_hw_volume = NULL; - s->get_hw_volume = NULL; - s->userdata = NULL; - - r = pa_idxset_put(core->sinks, s, &s->index); - assert(s->index != PA_IDXSET_INVALID && r >= 0); - - pa_sample_spec_snprint(st, sizeof(st), spec); - pa_log_info(__FILE__": created %u \"%s\" with sample spec \"%s\"\n", s->index, s->name, st); - - n = pa_sprintf_malloc("%s_monitor", name); - s->monitor_source = pa_source_new(core, driver, n, 0, spec, map); - assert(s->monitor_source); - pa_xfree(n); - s->monitor_source->monitor_of = s; - s->monitor_source->description = pa_sprintf_malloc("Monitor source of sink '%s'", s->name); - - pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index); - - return s; -} - -void pa_sink_disconnect(pa_sink* s) { - pa_sink_input *i, *j = NULL; - - assert(s); - assert(s->state == PA_SINK_RUNNING); - - pa_namereg_unregister(s->core, s->name); - - while ((i = pa_idxset_first(s->inputs, NULL))) { - assert(i != j); - pa_sink_input_kill(i); - j = i; - } - - pa_source_disconnect(s->monitor_source); - - pa_idxset_remove_by_data(s->core->sinks, s, NULL); - - s->get_latency = NULL; - s->notify = NULL; - s->get_hw_volume = NULL; - s->set_hw_volume = NULL; - - s->state = PA_SINK_DISCONNECTED; - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); -} - -static void sink_free(pa_sink *s) { - assert(s); - assert(!s->ref); - - if (s->state != PA_SINK_DISCONNECTED) - pa_sink_disconnect(s); - - pa_log_info(__FILE__": freed %u \"%s\"\n", s->index, s->name); - - pa_source_unref(s->monitor_source); - s->monitor_source = NULL; - - pa_idxset_free(s->inputs, NULL, NULL); - - pa_xfree(s->name); - pa_xfree(s->description); - pa_xfree(s->driver); - pa_xfree(s); -} - -void pa_sink_unref(pa_sink*s) { - assert(s); - assert(s->ref >= 1); - - if (!(--s->ref)) - sink_free(s); -} - -pa_sink* pa_sink_ref(pa_sink *s) { - assert(s); - assert(s->ref >= 1); - - s->ref++; - return s; -} - -void pa_sink_notify(pa_sink*s) { - assert(s); - assert(s->ref >= 1); - - if (s->notify) - s->notify(s); -} - -static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) { - uint32_t idx = PA_IDXSET_INVALID; - pa_sink_input *i; - unsigned n = 0; - - assert(s); - assert(s->ref >= 1); - assert(info); - - for (i = pa_idxset_first(s->inputs, &idx); maxinfo > 0 && i; i = pa_idxset_next(s->inputs, &idx)) { - /* Increase ref counter, to make sure that this input doesn't - * vanish while we still need it */ - pa_sink_input_ref(i); - - if (pa_sink_input_peek(i, &info->chunk, &info->volume) < 0) { - pa_sink_input_unref(i); - continue; - } - - info->userdata = i; - - assert(info->chunk.memblock); - assert(info->chunk.memblock->data); - assert(info->chunk.length); - - info++; - maxinfo--; - n++; - } - - return n; -} - -static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned maxinfo, size_t length) { - assert(s); - assert(s->ref >= 1); - assert(info); - - for (; maxinfo > 0; maxinfo--, info++) { - pa_sink_input *i = info->userdata; - - assert(i); - assert(info->chunk.memblock); - - /* Drop read data */ - pa_sink_input_drop(i, &info->chunk, length); - pa_memblock_unref(info->chunk.memblock); - - /* Decrease ref counter */ - pa_sink_input_unref(i); - info->userdata = NULL; - } -} - -int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { - pa_mix_info info[MAX_MIX_CHANNELS]; - unsigned n; - int r = -1; - - assert(s); - assert(s->ref >= 1); - assert(length); - assert(result); - - pa_sink_ref(s); - - n = fill_mix_info(s, info, MAX_MIX_CHANNELS); - - if (n <= 0) - goto finish; - - if (n == 1) { - pa_cvolume volume; - - *result = info[0].chunk; - pa_memblock_ref(result->memblock); - - if (result->length > length) - result->length = length; - - pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); - - if (!pa_cvolume_is_norm(&volume)) { - pa_memchunk_make_writable(result, s->core->memblock_stat, 0); - pa_volume_memchunk(result, &s->sample_spec, &volume); - } - } else { - result->memblock = pa_memblock_new(length, s->core->memblock_stat); - assert(result->memblock); - - result->length = pa_mix(info, n, result->memblock->data, length, &s->sample_spec, &s->sw_volume); - result->index = 0; - } - - inputs_drop(s, info, n, result->length); - pa_source_post(s->monitor_source, result); - - r = 0; - -finish: - pa_sink_unref(s); - - return r; -} - -int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { - pa_mix_info info[MAX_MIX_CHANNELS]; - unsigned n; - int r = -1; - - assert(s); - assert(s->ref >= 1); - assert(target); - assert(target->memblock); - assert(target->length); - assert(target->memblock->data); - - pa_sink_ref(s); - - n = fill_mix_info(s, info, MAX_MIX_CHANNELS); - - if (n <= 0) - goto finish; - - if (n == 1) { - pa_cvolume volume; - - if (target->length > info[0].chunk.length) - target->length = info[0].chunk.length; - - memcpy((uint8_t*) target->memblock->data + target->index, - (uint8_t*) info[0].chunk.memblock->data + info[0].chunk.index, - target->length); - - pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); - - if (!pa_cvolume_is_norm(&volume)) - pa_volume_memchunk(target, &s->sample_spec, &volume); - } else - target->length = pa_mix(info, n, - (uint8_t*) target->memblock->data + target->index, - target->length, - &s->sample_spec, - &s->sw_volume); - - inputs_drop(s, info, n, target->length); - pa_source_post(s->monitor_source, target); - - r = 0; - -finish: - pa_sink_unref(s); - - return r; -} - -void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { - pa_memchunk chunk; - size_t l, d; - - assert(s); - assert(s->ref >= 1); - assert(target); - assert(target->memblock); - assert(target->length); - assert(target->memblock->data); - - pa_sink_ref(s); - - l = target->length; - d = 0; - while (l > 0) { - chunk = *target; - chunk.index += d; - chunk.length -= d; - - if (pa_sink_render_into(s, &chunk) < 0) - break; - - d += chunk.length; - l -= chunk.length; - } - - if (l > 0) { - chunk = *target; - chunk.index += d; - chunk.length -= d; - pa_silence_memchunk(&chunk, &s->sample_spec); - } - - pa_sink_unref(s); -} - -void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { - assert(s); - assert(s->ref >= 1); - assert(length); - assert(result); - - /*** This needs optimization ***/ - - result->memblock = pa_memblock_new(result->length = length, s->core->memblock_stat); - result->index = 0; - - pa_sink_render_into_full(s, result); -} - -pa_usec_t pa_sink_get_latency(pa_sink *s) { - assert(s); - assert(s->ref >= 1); - - if (!s->get_latency) - return 0; - - return s->get_latency(s); -} - -void pa_sink_set_owner(pa_sink *s, pa_module *m) { - assert(s); - assert(s->ref >= 1); - - s->owner = m; - - if (s->monitor_source) - pa_source_set_owner(s->monitor_source, m); -} - -void pa_sink_set_volume(pa_sink *s, pa_mixer_t m, const pa_cvolume *volume) { - pa_cvolume *v; - - assert(s); - assert(s->ref >= 1); - assert(volume); - - if (m == PA_MIXER_HARDWARE && s->set_hw_volume) - v = &s->hw_volume; - else - v = &s->sw_volume; - - if (pa_cvolume_equal(v, volume)) - return; - - *v = *volume; - - if (v == &s->hw_volume) - if (s->set_hw_volume(s) < 0) - s->sw_volume = *volume; - - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); -} - -const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_mixer_t m) { - assert(s); - assert(s->ref >= 1); - - if (m == PA_MIXER_HARDWARE && s->set_hw_volume) { - - if (s->get_hw_volume) - s->get_hw_volume(s); - - return &s->hw_volume; - } else - return &s->sw_volume; -} diff --git a/polyp/sink.h b/polyp/sink.h deleted file mode 100644 index 33aba233..00000000 --- a/polyp/sink.h +++ /dev/null @@ -1,100 +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. -***/ - -#include - -typedef struct pa_sink pa_sink; - -#include "core.h" -#include "sample.h" -#include "idxset.h" -#include "source.h" -#include "channelmap.h" -#include "module.h" -#include "volume.h" - -#define PA_MAX_INPUTS_PER_SINK 6 - -typedef enum pa_sink_state { - PA_SINK_RUNNING, - PA_SINK_DISCONNECTED -} pa_sink_state_t; - -typedef enum pa_mixer { - PA_MIXER_SOFTWARE, - PA_MIXER_HARDWARE -} pa_mixer_t; - -struct pa_sink { - int ref; - uint32_t index; - pa_core *core; - pa_sink_state_t state; - - char *name, *description, *driver; - pa_module *owner; - - pa_sample_spec sample_spec; - pa_channel_map channel_map; - - pa_idxset *inputs; - pa_source *monitor_source; - - pa_cvolume hw_volume, sw_volume; - - void (*notify)(pa_sink*sink); - pa_usec_t (*get_latency)(pa_sink *s); - int (*set_hw_volume)(pa_sink *s); - int (*get_hw_volume)(pa_sink *s); - - void *userdata; -}; - -pa_sink* pa_sink_new( - pa_core *core, - const char *driver, - const char *name, - int namereg_fail, - const pa_sample_spec *spec, - const pa_channel_map *map); - -void pa_sink_disconnect(pa_sink* s); -void pa_sink_unref(pa_sink*s); -pa_sink* pa_sink_ref(pa_sink *s); - -int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result); -void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result); -int pa_sink_render_into(pa_sink*s, pa_memchunk *target); -void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target); - -pa_usec_t pa_sink_get_latency(pa_sink *s); - -void pa_sink_notify(pa_sink*s); - -void pa_sink_set_owner(pa_sink *sink, pa_module *m); - -void pa_sink_set_volume(pa_sink *sink, pa_mixer_t m, const pa_cvolume *volume); -const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_mixer_t m); - -#endif diff --git a/polyp/sioman.c b/polyp/sioman.c deleted file mode 100644 index 8d7b136c..00000000 --- a/polyp/sioman.c +++ /dev/null @@ -1,42 +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 -#endif - -#include -#include "sioman.h" - -static int stdio_inuse = 0; - -int pa_stdio_acquire(void) { - if (stdio_inuse) - return -1; - - stdio_inuse = 1; - return 0; -} - -void pa_stdio_release(void) { - assert(stdio_inuse); - stdio_inuse = 0; -} diff --git a/polyp/sioman.h b/polyp/sioman.h deleted file mode 100644 index 840d93f2..00000000 --- a/polyp/sioman.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef foosiomanhfoo -#define foosiomanhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -int pa_stdio_acquire(void); -void pa_stdio_release(void); - -#endif diff --git a/polyp/socket-client.c b/polyp/socket-client.c deleted file mode 100644 index 29c9775e..00000000 --- a/polyp/socket-client.c +++ /dev/null @@ -1,513 +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 -#endif - -/* #undef HAVE_LIBASYNCNS */ - -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SYS_SOCKET_H -#include -#endif -#ifdef HAVE_SYS_UN_H -#include -#endif -#ifdef HAVE_ARPA_INET_H -#include -#endif -#ifdef HAVE_NETINET_IN_H -#include -#endif -#ifdef HAVE_NETDB_H -#include -#endif - -#ifdef HAVE_LIBASYNCNS -#include -#endif - -#include "winsock.h" - -#include "socket-client.h" -#include "socket-util.h" -#include "util.h" -#include "xmalloc.h" -#include "log.h" -#include "parseaddr.h" - -#define CONNECT_TIMEOUT 5 - -struct pa_socket_client { - int ref; - pa_mainloop_api *mainloop; - int fd; - pa_io_event *io_event; - pa_time_event *timeout_event; - pa_defer_event *defer_event; - void (*callback)(pa_socket_client*c, pa_iochannel *io, void *userdata); - void *userdata; - int local; -#ifdef HAVE_LIBASYNCNS - asyncns_t *asyncns; - asyncns_query_t * asyncns_query; - pa_io_event *asyncns_io_event; -#endif -}; - -static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) { - pa_socket_client *c; - assert(m); - - c = pa_xmalloc(sizeof(pa_socket_client)); - c->ref = 1; - c->mainloop = m; - c->fd = -1; - c->io_event = NULL; - c->defer_event = NULL; - c->timeout_event = NULL; - c->callback = NULL; - c->userdata = NULL; - c->local = 0; - -#ifdef HAVE_LIBASYNCNS - c->asyncns = NULL; - c->asyncns_io_event = NULL; - c->asyncns_query = NULL; -#endif - - return c; -} - -static void free_events(pa_socket_client *c) { - assert(c); - - if (c->io_event) { - c->mainloop->io_free(c->io_event); - c->io_event = NULL; - } - - if (c->defer_event) { - c->mainloop->defer_free(c->defer_event); - c->defer_event = NULL; - } - - if (c->timeout_event) { - c->mainloop->time_free(c->timeout_event); - c->timeout_event = NULL; - } -} - -static void do_call(pa_socket_client *c) { - pa_iochannel *io = NULL; - int error; - socklen_t lerror; - assert(c && c->callback); - - pa_socket_client_ref(c); - - if (c->fd < 0) - goto finish; - - lerror = sizeof(error); - if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void*)&error, &lerror) < 0) { - pa_log(__FILE__": getsockopt(): %s\n", strerror(errno)); - goto finish; - } - - if (lerror != sizeof(error)) { - pa_log(__FILE__": getsockopt() returned invalid size.\n"); - goto finish; - } - - if (error != 0) { - pa_log_debug(__FILE__": connect(): %s\n", strerror(error)); - errno = error; - goto finish; - } - - io = pa_iochannel_new(c->mainloop, c->fd, c->fd); - assert(io); - -finish: - if (!io && c->fd >= 0) - close(c->fd); - c->fd = -1; - - free_events(c); - - assert(c->callback); - c->callback(c, io, c->userdata); - - pa_socket_client_unref(c); -} - -static void connect_fixed_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { - pa_socket_client *c = userdata; - assert(m && c && c->defer_event == e); - do_call(c); -} - -static void connect_io_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { - pa_socket_client *c = userdata; - assert(m && c && c->io_event == e && fd >= 0); - do_call(c); -} - -static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t len) { - int r; - assert(c && sa && len); - - pa_make_nonblock_fd(c->fd); - - if ((r = connect(c->fd, sa, len)) < 0) { - if (errno != EINPROGRESS) { - /*pa_log(__FILE__": connect(): %s\n", strerror(errno));*/ - return -1; - } - - c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c); - assert(c->io_event); - } else { - c->defer_event = c->mainloop->defer_new(c->mainloop, connect_fixed_cb, c); - assert(c->defer_event); - } - - return 0; -} - -pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port) { - struct sockaddr_in sa; - assert(m && port > 0); - - memset(&sa, 0, sizeof(sa)); - sa.sin_family = AF_INET; - sa.sin_port = htons(port); - sa.sin_addr.s_addr = htonl(address); - - return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa)); -} - -#ifdef HAVE_SYS_UN_H - -pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename) { - struct sockaddr_un sa; - assert(m && filename); - - memset(&sa, 0, sizeof(sa)); - sa.sun_family = AF_UNIX; - strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1); - sa.sun_path[sizeof(sa.sun_path) - 1] = 0; - - return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa)); -} - -#else /* HAVE_SYS_UN_H */ - -pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename) { - return NULL; -} - -#endif /* HAVE_SYS_UN_H */ - -static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size_t salen) { - assert(c); - assert(sa); - assert(salen); - - switch (sa->sa_family) { - case AF_UNIX: - c->local = 1; - break; - - case AF_INET: - c->local = ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK; - break; - - case AF_INET6: - c->local = memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0; - break; - - default: - c->local = 0; - } - - if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) { - pa_log(__FILE__": socket(): %s\n", strerror(errno)); - return -1; - } - - pa_fd_set_cloexec(c->fd, 1); - if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6) - pa_socket_tcp_low_delay(c->fd); - else - pa_socket_low_delay(c->fd); - - if (do_connect(c, sa, salen) < 0) - return -1; - - return 0; -} - -pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen) { - pa_socket_client *c; - assert(m && sa); - c = pa_socket_client_new(m); - assert(c); - - if (sockaddr_prepare(c, sa, salen) < 0) - goto fail; - - return c; - -fail: - pa_socket_client_unref(c); - return NULL; - -} - -static void socket_client_free(pa_socket_client *c) { - assert(c && c->mainloop); - - - free_events(c); - - if (c->fd >= 0) - close(c->fd); - -#ifdef HAVE_LIBASYNCNS - if (c->asyncns_query) - asyncns_cancel(c->asyncns, c->asyncns_query); - if (c->asyncns) - asyncns_free(c->asyncns); - if (c->asyncns_io_event) - c->mainloop->io_free(c->asyncns_io_event); -#endif - - pa_xfree(c); -} - -void pa_socket_client_unref(pa_socket_client *c) { - assert(c && c->ref >= 1); - - if (!(--(c->ref))) - socket_client_free(c); -} - -pa_socket_client* pa_socket_client_ref(pa_socket_client *c) { - assert(c && c->ref >= 1); - c->ref++; - return c; -} - -void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa_socket_client *c, pa_iochannel*io, void *userdata), void *userdata) { - assert(c); - c->callback = on_connection; - c->userdata = userdata; -} - -pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port) { - struct sockaddr_in6 sa; - - memset(&sa, 0, sizeof(sa)); - sa.sin6_family = AF_INET6; - sa.sin6_port = htons(port); - memcpy(&sa.sin6_addr, address, sizeof(sa.sin6_addr)); - - return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa)); -} - -#ifdef HAVE_LIBASYNCNS - -static void asyncns_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { - pa_socket_client *c = userdata; - struct addrinfo *res = NULL; - int ret; - assert(m && c && c->asyncns_io_event == e && fd >= 0); - - if (asyncns_wait(c->asyncns, 0) < 0) - goto fail; - - if (!asyncns_isdone(c->asyncns, c->asyncns_query)) - return; - - ret = asyncns_getaddrinfo_done(c->asyncns, c->asyncns_query, &res); - c->asyncns_query = NULL; - - if (ret != 0 || !res) - goto fail; - - if (res->ai_addr) - sockaddr_prepare(c, res->ai_addr, res->ai_addrlen); - - asyncns_freeaddrinfo(res); - - goto finish; - -fail: - errno == EHOSTUNREACH; - do_call(c); - -finish: - - m->io_free(c->asyncns_io_event); - c->asyncns_io_event = NULL; -} - -#endif - -static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeval *tv, void *userdata) { - pa_socket_client *c = userdata; - assert(m); - assert(e); - assert(tv); - assert(c); - - if (c->fd >= 0) { - close(c->fd); - c->fd = -1; - } - - errno = ETIMEDOUT; - do_call(c); -} - -static void start_timeout(pa_socket_client *c) { - struct timeval tv; - assert(c); - assert(!c->timeout_event); - - pa_gettimeofday(&tv); - pa_timeval_add(&tv, CONNECT_TIMEOUT * 1000000); - c->timeout_event = c->mainloop->time_new(c->mainloop, &tv, timeout_cb, c); -} - -pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*name, uint16_t default_port) { - pa_socket_client *c = NULL; - pa_parsed_address a; - assert(m && name); - - if (pa_parse_address(name, &a) < 0) - return NULL; - - if (!a.port) - a.port = default_port; - - switch (a.type) { - case PA_PARSED_ADDRESS_UNIX: - if ((c = pa_socket_client_new_unix(m, a.path_or_host))) - start_timeout(c); - break; - - case PA_PARSED_ADDRESS_TCP4: /* Fallthrough */ - case PA_PARSED_ADDRESS_TCP6: /* Fallthrough */ - case PA_PARSED_ADDRESS_TCP_AUTO:{ - - struct addrinfo hints; - char port[12]; - - snprintf(port, sizeof(port), "%u", (unsigned) a.port); - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = a.type == PA_PARSED_ADDRESS_TCP4 ? PF_INET : (a.type == PA_PARSED_ADDRESS_TCP6 ? PF_INET6 : PF_UNSPEC); - hints.ai_socktype = SOCK_STREAM; - -#ifdef HAVE_LIBASYNCNS - { - asyncns_t *asyncns; - - if (!(asyncns = asyncns_new(1))) - goto finish; - - c = pa_socket_client_new(m); - c->asyncns = asyncns; - c->asyncns_io_event = m->io_new(m, asyncns_fd(c->asyncns), PA_IO_EVENT_INPUT, asyncns_cb, c); - c->asyncns_query = asyncns_getaddrinfo(c->asyncns, a.path_or_host, port, &hints); - assert(c->asyncns_query); - start_timeout(c); - } -#else /* HAVE_LIBASYNCNS */ - { -#ifdef HAVE_GETADDRINFO - int ret; - struct addrinfo *res = NULL; - - ret = getaddrinfo(a.path_or_host, port, &hints, &res); - - if (ret < 0 || !res) - goto finish; - - if (res->ai_addr) { - if ((c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen))) - start_timeout(c); - } - - freeaddrinfo(res); -#else /* HAVE_GETADDRINFO */ - struct hostent *host = NULL; - struct sockaddr_in s; - - /* FIXME: PF_INET6 support */ - if (hints.ai_family != PF_INET) - goto finish; - - host = gethostbyname(a.path_or_host); - if (!host) { - unsigned int addr = inet_addr(a.path_or_host); - if (addr != INADDR_NONE) - host = gethostbyaddr((char*)&addr, 4, AF_INET); - } - - if (!host) - goto finish; - - s.sin_family = AF_INET; - memcpy(&s.sin_addr, host->h_addr, sizeof(struct in_addr)); - s.sin_port = port; - - if ((c = pa_socket_client_new_sockaddr(m, &s, sizeof(s)))) - start_timeout(c); -#endif /* HAVE_GETADDRINFO */ - } -#endif /* HAVE_LIBASYNCNS */ - } - } - -finish: - pa_xfree(a.path_or_host); - return c; - -} - -/* Return non-zero when the target sockaddr is considered - local. "local" means UNIX socket or TCP socket on localhost. Other - local IP addresses are not considered local. */ -int pa_socket_client_is_local(pa_socket_client *c) { - assert(c); - return c->local; -} diff --git a/polyp/socket-client.h b/polyp/socket-client.h deleted file mode 100644 index 40e9629a..00000000 --- a/polyp/socket-client.h +++ /dev/null @@ -1,47 +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 - -#include "mainloop-api.h" -#include "iochannel.h" - -struct sockaddr; - -typedef struct pa_socket_client pa_socket_client; - -pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port); -pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port); -pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename); -pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen); -pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char *a, uint16_t default_port); - -void pa_socket_client_unref(pa_socket_client *c); -pa_socket_client* pa_socket_client_ref(pa_socket_client *c); - -void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa_socket_client *c, pa_iochannel*io, void *userdata), void *userdata); - -int pa_socket_client_is_local(pa_socket_client *c); - -#endif diff --git a/polyp/socket-server.c b/polyp/socket-server.c deleted file mode 100644 index 262b32a7..00000000 --- a/polyp/socket-server.c +++ /dev/null @@ -1,432 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SYS_SOCKET_H -#include -#endif -#ifdef HAVE_SYS_UN_H -#include -#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 -#endif -#ifdef HAVE_NETINET_IN_H -#include -#endif - -#ifdef HAVE_LIBWRAP -#include -#endif - -#ifndef HAVE_INET_NTOP -#include "inet_ntop.h" -#endif - -#include "winsock.h" - -#include "socket-server.h" -#include "socket-util.h" -#include "xmalloc.h" -#include "util.h" -#include "log.h" - -struct pa_socket_server { - int ref; - int fd; - char *filename; - char *tcpwrap_service; - - void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata); - void *userdata; - - pa_io_event *io_event; - pa_mainloop_api *mainloop; - enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX, SOCKET_SERVER_IPV6 } type; -}; - -static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { - pa_socket_server *s = userdata; - pa_iochannel *io; - int nfd; - assert(s && s->mainloop == mainloop && s->io_event == e && e && fd >= 0 && fd == s->fd); - - pa_socket_server_ref(s); - - if ((nfd = accept(fd, NULL, NULL)) < 0) { - pa_log(__FILE__": accept(): %s\n", strerror(errno)); - goto finish; - } - - pa_fd_set_cloexec(nfd, 1); - - if (!s->on_connection) { - close(nfd); - goto finish; - } - -#ifdef HAVE_LIBWRAP - - if (s->type == SOCKET_SERVER_IPV4 && s->tcpwrap_service) { - struct request_info req; - - request_init(&req, RQ_DAEMON, s->tcpwrap_service, RQ_FILE, nfd, NULL); - fromhost(&req); - if (!hosts_access(&req)) { - pa_log(__FILE__": TCP connection refused by tcpwrap.\n"); - close(nfd); - goto finish; - } - - pa_log(__FILE__": TCP connection accepted by tcpwrap.\n"); - } -#endif - - /* There should be a check for socket type here */ - if (s->type == SOCKET_SERVER_IPV4) - pa_socket_tcp_low_delay(fd); - else - pa_socket_low_delay(fd); - - io = pa_iochannel_new(s->mainloop, nfd, nfd); - assert(io); - s->on_connection(s, io, s->userdata); - -finish: - pa_socket_server_unref(s); -} - -pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) { - pa_socket_server *s; - assert(m && fd >= 0); - - s = pa_xmalloc(sizeof(pa_socket_server)); - s->ref = 1; - s->fd = fd; - s->filename = NULL; - s->on_connection = NULL; - s->userdata = NULL; - s->tcpwrap_service = NULL; - - s->mainloop = m; - s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s); - assert(s->io_event); - - s->type = SOCKET_SERVER_GENERIC; - - return s; -} - -pa_socket_server* pa_socket_server_ref(pa_socket_server *s) { - assert(s && s->ref >= 1); - s->ref++; - return s; -} - -#ifdef HAVE_SYS_UN_H - -pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) { - int fd = -1; - struct sockaddr_un sa; - pa_socket_server *s; - - assert(m && filename); - - if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { - pa_log(__FILE__": socket(): %s\n", strerror(errno)); - goto fail; - } - - pa_fd_set_cloexec(fd, 1); - - sa.sun_family = AF_UNIX; - strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1); - sa.sun_path[sizeof(sa.sun_path) - 1] = 0; - - pa_socket_low_delay(fd); - - if (bind(fd, (struct sockaddr*) &sa, SUN_LEN(&sa)) < 0) { - pa_log(__FILE__": bind(): %s\n", strerror(errno)); - goto fail; - } - - if (listen(fd, 5) < 0) { - pa_log(__FILE__": listen(): %s\n", strerror(errno)); - goto fail; - } - - s = pa_socket_server_new(m, fd); - assert(s); - - s->filename = pa_xstrdup(filename); - s->type = SOCKET_SERVER_UNIX; - - return s; - -fail: - if (fd >= 0) - close(fd); - - return NULL; -} - -#else /* HAVE_SYS_UN_H */ - -pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) { - return NULL; -} - -#endif /* HAVE_SYS_UN_H */ - -pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port, const char *tcpwrap_service) { - pa_socket_server *ss; - int fd = -1; - struct sockaddr_in sa; - int on = 1; - - assert(m && port); - - if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { - pa_log(__FILE__": socket(): %s\n", strerror(errno)); - goto fail; - } - - pa_fd_set_cloexec(fd, 1); - - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0) - pa_log(__FILE__": setsockopt(): %s\n", strerror(errno)); - - pa_socket_tcp_low_delay(fd); - - memset(&sa, 0, sizeof(sa)); - sa.sin_family = AF_INET; - sa.sin_port = htons(port); - sa.sin_addr.s_addr = htonl(address); - - if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { - pa_log(__FILE__": bind(): %s\n", strerror(errno)); - goto fail; - } - - if (listen(fd, 5) < 0) { - pa_log(__FILE__": listen(): %s\n", strerror(errno)); - goto fail; - } - - if ((ss = pa_socket_server_new(m, fd))) { - ss->type = SOCKET_SERVER_IPV4; - ss->tcpwrap_service = pa_xstrdup(tcpwrap_service); - } - - return ss; - -fail: - if (fd >= 0) - close(fd); - - return NULL; -} - -pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port) { - pa_socket_server *ss; - int fd = -1; - struct sockaddr_in6 sa; - int on = 1; - - assert(m && port); - - if ((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0) { - pa_log(__FILE__": socket(): %s\n", strerror(errno)); - goto fail; - } - - pa_fd_set_cloexec(fd, 1); - - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0) - pa_log(__FILE__": setsockopt(): %s\n", strerror(errno)); - - pa_socket_tcp_low_delay(fd); - - memset(&sa, 0, sizeof(sa)); - sa.sin6_family = AF_INET6; - sa.sin6_port = htons(port); - memcpy(sa.sin6_addr.s6_addr, address, 16); - - if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { - pa_log(__FILE__": bind(): %s\n", strerror(errno)); - goto fail; - } - - if (listen(fd, 5) < 0) { - pa_log(__FILE__": listen(): %s\n", strerror(errno)); - goto fail; - } - - if ((ss = pa_socket_server_new(m, fd))) - ss->type = SOCKET_SERVER_IPV6; - - return ss; - -fail: - if (fd >= 0) - close(fd); - - return NULL; -} - -static void socket_server_free(pa_socket_server*s) { - assert(s); - close(s->fd); - - if (s->filename) { - unlink(s->filename); - pa_xfree(s->filename); - } - - pa_xfree(s->tcpwrap_service); - - s->mainloop->io_free(s->io_event); - pa_xfree(s); -} - -void pa_socket_server_unref(pa_socket_server *s) { - assert(s && s->ref >= 1); - - if (!(--(s->ref))) - socket_server_free(s); -} - -void pa_socket_server_set_callback(pa_socket_server*s, void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata), void *userdata) { - assert(s && s->ref >= 1); - - s->on_connection = on_connection; - s->userdata = userdata; -} - - -char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) { - assert(s && c && l > 0); - - switch (s->type) { - case SOCKET_SERVER_IPV6: { - struct sockaddr_in6 sa; - socklen_t sa_len = sizeof(sa); - - if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) { - pa_log(__FILE__": getsockname() failed: %s\n", strerror(errno)); - return NULL; - } - - if (memcmp(&in6addr_any, &sa.sin6_addr, sizeof(in6addr_any)) == 0) { - char fqdn[256]; - if (!pa_get_fqdn(fqdn, sizeof(fqdn))) - return NULL; - - snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port)); - - } else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) { - char hn[256]; - if (!pa_get_host_name(hn, sizeof(hn))) - return NULL; - - snprintf(c, l, "{%s}tcp6:localhost:%u", hn, (unsigned) ntohs(sa.sin6_port)); - } else { - char ip[INET6_ADDRSTRLEN]; - - if (!inet_ntop(AF_INET6, &sa.sin6_addr, ip, sizeof(ip))) { - pa_log(__FILE__": inet_ntop() failed: %s\n", strerror(errno)); - return NULL; - } - - snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port)); - } - - return c; - } - - case SOCKET_SERVER_IPV4: { - struct sockaddr_in sa; - socklen_t sa_len = sizeof(sa); - - if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) { - pa_log(__FILE__": getsockname() failed: %s\n", strerror(errno)); - return NULL; - } - - if (sa.sin_addr.s_addr == INADDR_ANY) { - char fqdn[256]; - if (!pa_get_fqdn(fqdn, sizeof(fqdn))) - return NULL; - - snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port)); - } else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) { - char hn[256]; - if (!pa_get_host_name(hn, sizeof(hn))) - return NULL; - - snprintf(c, l, "{%s}tcp:localhost:%u", hn, (unsigned) ntohs(sa.sin_port)); - } else { - char ip[INET_ADDRSTRLEN]; - - if (!inet_ntop(AF_INET, &sa.sin_addr, ip, sizeof(ip))) { - pa_log(__FILE__": inet_ntop() failed: %s\n", strerror(errno)); - return NULL; - } - - snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port)); - - } - - return c; - } - - case SOCKET_SERVER_UNIX: { - char hn[256]; - - if (!s->filename) - return NULL; - - if (!pa_get_host_name(hn, sizeof(hn))) - return NULL; - - snprintf(c, l, "{%s}unix:%s", hn, s->filename); - return c; - } - - default: - return NULL; - } -} diff --git a/polyp/socket-server.h b/polyp/socket-server.h deleted file mode 100644 index 6c5d7bac..00000000 --- a/polyp/socket-server.h +++ /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 -#include "mainloop-api.h" -#include "iochannel.h" - -/* It is safe to destroy the calling socket_server object from the callback */ - -typedef struct pa_socket_server pa_socket_server; - -pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd); -pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename); -pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port, const char *tcpwrap_service); -pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port); - -void pa_socket_server_unref(pa_socket_server*s); -pa_socket_server* pa_socket_server_ref(pa_socket_server *s); - -void pa_socket_server_set_callback(pa_socket_server*s, void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata), void *userdata); - -char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l); - -#endif diff --git a/polyp/socket-util.c b/polyp/socket-util.c deleted file mode 100644 index 032a3c91..00000000 --- a/polyp/socket-util.c +++ /dev/null @@ -1,246 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SYS_SOCKET_H -#include -#endif -#ifdef HAVE_SYS_UN_H -#include -#endif -#ifdef HAVE_NETINET_IN_H -#include -#endif -#ifdef HAVE_NETINET_IN_SYSTM_H -#include -#endif -#ifdef HAVE_NETINET_IP_H -#include -#endif -#ifdef HAVE_NETINET_TCP_H -#include -#endif -#ifdef HAVE_NETDB_H -#include -#endif - -#include "winsock.h" - -#include "socket-util.h" -#include "util.h" -#include "xmalloc.h" -#include "log.h" - -void pa_socket_peer_to_string(int fd, char *c, size_t l) { - struct stat st; - - assert(c && l && fd >= 0); - -#ifndef OS_IS_WIN32 - if (fstat(fd, &st) < 0) { - snprintf(c, l, "Invalid client fd"); - return; - } -#endif - -#ifndef OS_IS_WIN32 - if (S_ISSOCK(st.st_mode)) { -#endif - union { - struct sockaddr sa; - struct sockaddr_in in; -#ifdef HAVE_SYS_UN_H - struct sockaddr_un un; -#endif - } sa; - socklen_t sa_len = sizeof(sa); - - if (getpeername(fd, &sa.sa, &sa_len) >= 0) { - - if (sa.sa.sa_family == AF_INET) { - uint32_t ip = ntohl(sa.in.sin_addr.s_addr); - - snprintf(c, l, "TCP/IP client from %i.%i.%i.%i:%u", - ip >> 24, - (ip >> 16) & 0xFF, - (ip >> 8) & 0xFF, - ip & 0xFF, - ntohs(sa.in.sin_port)); - return; -#ifdef HAVE_SYS_UN_H - } else if (sa.sa.sa_family == AF_UNIX) { - snprintf(c, l, "UNIX socket client"); - return; -#endif - } - - } -#ifndef OS_IS_WIN32 - snprintf(c, l, "Unknown network client"); - return; - } else if (S_ISCHR(st.st_mode) && (fd == 0 || fd == 1)) { - snprintf(c, l, "STDIN/STDOUT client"); - return; - } -#endif /* OS_IS_WIN32 */ - - snprintf(c, l, "Unknown client"); -} - -int pa_socket_low_delay(int fd) { -#ifdef SO_PRIORITY - int priority; - assert(fd >= 0); - - priority = 7; - if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (void*)&priority, sizeof(priority)) < 0) - return -1; -#endif - - return 0; -} - -int pa_socket_tcp_low_delay(int fd) { - int ret, tos, on; - - assert(fd >= 0); - - ret = pa_socket_low_delay(fd); - - on = 1; - tos = 0; - -#if defined(SOL_TCP) || defined(IPPROTO_TCP) -#if defined(SOL_TCP) - if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0) -#else - if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0) -#endif - ret = -1; -#endif - -#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || \ - defined(IPPROTO_IP)) - tos = IPTOS_LOWDELAY; -#ifdef SOL_IP - if (setsockopt(fd, SOL_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0) -#else - if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0) -#endif - ret = -1; -#endif - - return ret; - -} - -int pa_socket_set_rcvbuf(int fd, size_t l) { - assert(fd >= 0); - -/* if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&l, sizeof(l)) < 0) { */ -/* pa_log(__FILE__": SO_RCVBUF: %s\n", strerror(errno)); */ -/* return -1; */ -/* } */ - - return 0; -} - -int pa_socket_set_sndbuf(int fd, size_t l) { - assert(fd >= 0); - -/* if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&l, sizeof(l)) < 0) { */ -/* pa_log(__FILE__": SO_SNDBUF: %s\n", strerror(errno)); */ -/* return -1; */ -/* } */ - - return 0; -} - -#ifdef HAVE_SYS_UN_H - -int pa_unix_socket_is_stale(const char *fn) { - struct sockaddr_un sa; - int fd = -1, ret = -1; - - if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { - pa_log(__FILE__": socket(): %s\n", strerror(errno)); - goto finish; - } - - sa.sun_family = AF_UNIX; - strncpy(sa.sun_path, fn, sizeof(sa.sun_path)-1); - sa.sun_path[sizeof(sa.sun_path) - 1] = 0; - - if (connect(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) { - if (errno == ECONNREFUSED) - ret = 1; - } else - ret = 0; - -finish: - if (fd >= 0) - close(fd); - - return ret; -} - -int pa_unix_socket_remove_stale(const char *fn) { - int r; - - if ((r = pa_unix_socket_is_stale(fn)) < 0) - return errno != ENOENT ? -1 : 0; - - if (!r) - return 0; - - /* Yes, here is a race condition. But who cares? */ - if (unlink(fn) < 0) - return -1; - - return 0; -} - -#else /* HAVE_SYS_UN_H */ - -int pa_unix_socket_is_stale(const char *fn) { - return -1; -} - -int pa_unix_socket_remove_stale(const char *fn) { - return -1; -} - -#endif /* HAVE_SYS_UN_H */ diff --git a/polyp/socket-util.h b/polyp/socket-util.h deleted file mode 100644 index ae16fb16..00000000 --- a/polyp/socket-util.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef foosocketutilhfoo -#define foosocketutilhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -void pa_socket_peer_to_string(int fd, char *c, size_t l); - -int pa_socket_low_delay(int fd); -int pa_socket_tcp_low_delay(int fd); - -int pa_socket_set_sndbuf(int fd, size_t l); -int pa_socket_set_rcvbuf(int fd, size_t l); - -int pa_unix_socket_is_stale(const char *fn); -int pa_unix_socket_remove_stale(const char *fn); - -#endif diff --git a/polyp/sound-file-stream.c b/polyp/sound-file-stream.c deleted file mode 100644 index 881e3077..00000000 --- a/polyp/sound-file-stream.c +++ /dev/null @@ -1,179 +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 -#endif - -#include -#include -#include -#include - -#include - -#include "sound-file-stream.h" -#include "sink-input.h" -#include "xmalloc.h" -#include "log.h" - -#define BUF_SIZE (1024*10) - -struct userdata { - SNDFILE *sndfile; - pa_sink_input *sink_input; - pa_memchunk memchunk; - sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames); -}; - -static void free_userdata(struct userdata *u) { - assert(u); - if (u->sink_input) { - pa_sink_input_disconnect(u->sink_input); - pa_sink_input_unref(u->sink_input); - } - - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); - if (u->sndfile) - sf_close(u->sndfile); - - pa_xfree(u); -} - -static void sink_input_kill(pa_sink_input *i) { - assert(i && i->userdata); - free_userdata(i->userdata); -} - -static void si_kill(PA_GCC_UNUSED pa_mainloop_api *m, void *i) { - sink_input_kill(i); -} - -static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) { - struct userdata *u; - assert(i && chunk && i->userdata); - u = i->userdata; - - if (!u->memchunk.memblock) { - uint32_t fs = pa_frame_size(&i->sample_spec); - sf_count_t samples = BUF_SIZE/fs; - - u->memchunk.memblock = pa_memblock_new(BUF_SIZE, i->sink->core->memblock_stat); - u->memchunk.index = 0; - samples = u->readf_function(u->sndfile, u->memchunk.memblock->data, samples); - u->memchunk.length = samples*fs; - - if (!u->memchunk.length) { - pa_memblock_unref(u->memchunk.memblock); - u->memchunk.memblock = NULL; - u->memchunk.index = u->memchunk.length = 0; - pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i); - return -1; - } - } - - *chunk = u->memchunk; - pa_memblock_ref(chunk->memblock); - assert(chunk->length); - return 0; -} - -static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) { - struct userdata *u; - assert(i && chunk && length && i->userdata); - u = i->userdata; - - assert(!memcmp(chunk, &u->memchunk, sizeof(chunk))); - assert(length <= u->memchunk.length); - - u->memchunk.index += length; - u->memchunk.length -= length; - - if (u->memchunk.length <= 0) { - pa_memblock_unref(u->memchunk.memblock); - u->memchunk.memblock = NULL; - u->memchunk.index = u->memchunk.length = 0; - } -} - -int pa_play_file(pa_sink *sink, const char *fname, const pa_cvolume *volume) { - struct userdata *u = NULL; - SF_INFO sfinfo; - pa_sample_spec ss; - assert(sink && fname); - - u = pa_xmalloc(sizeof(struct userdata)); - u->sink_input = NULL; - u->memchunk.memblock = NULL; - u->memchunk.index = u->memchunk.length = 0; - u->sndfile = NULL; - - memset(&sfinfo, 0, sizeof(sfinfo)); - - if (!(u->sndfile = sf_open(fname, SFM_READ, &sfinfo))) { - pa_log(__FILE__": Failed to open file %s\n", fname); - goto fail; - } - - switch (sfinfo.format & 0xFF) { - case SF_FORMAT_PCM_16: - case SF_FORMAT_PCM_U8: - case SF_FORMAT_ULAW: - case SF_FORMAT_ALAW: - ss.format = PA_SAMPLE_S16NE; - u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short; - break; - case SF_FORMAT_FLOAT: - default: - ss.format = PA_SAMPLE_FLOAT32NE; - u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float; - break; - } - - ss.rate = sfinfo.samplerate; - ss.channels = sfinfo.channels; - - if (!pa_sample_spec_valid(&ss)) { - pa_log(__FILE__": Unsupported sample format in file %s\n", fname); - goto fail; - } - - if (!(u->sink_input = pa_sink_input_new(sink, __FILE__, fname, &ss, NULL, 0, -1))) - goto fail; - - if (volume) - u->sink_input->volume = *volume; - u->sink_input->peek = sink_input_peek; - u->sink_input->drop = sink_input_drop; - u->sink_input->kill = sink_input_kill; - u->sink_input->userdata = u; - - pa_sink_notify(sink); - - return 0; - -fail: - if (u) - free_userdata(u); - - return -1; -} diff --git a/polyp/sound-file-stream.h b/polyp/sound-file-stream.h deleted file mode 100644 index 2e56ef49..00000000 --- a/polyp/sound-file-stream.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef foosoundfilestreamhfoo -#define foosoundfilestreamhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "sink.h" - -int pa_play_file(pa_sink *sink, const char *fname, const pa_cvolume *volume); - -#endif diff --git a/polyp/sound-file.c b/polyp/sound-file.c deleted file mode 100644 index 0048ed74..00000000 --- a/polyp/sound-file.c +++ /dev/null @@ -1,139 +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 -#endif - -#include -#include - -#include - -#include "sound-file.h" -#include "sample.h" -#include "log.h" - -#define MAX_FILE_SIZE (1024*1024) - -int pa_sound_file_load(const char *fname, pa_sample_spec *ss, pa_memchunk *chunk, pa_memblock_stat *s) { - SNDFILE*sf = NULL; - SF_INFO sfinfo; - int ret = -1; - size_t l; - sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames); - assert(fname && ss && chunk); - - chunk->memblock = NULL; - chunk->index = chunk->length = 0; - - memset(&sfinfo, 0, sizeof(sfinfo)); - - if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) { - pa_log(__FILE__": Failed to open file %s\n", fname); - goto finish; - } - - switch (sfinfo.format & SF_FORMAT_SUBMASK) { - case SF_FORMAT_FLOAT: - case SF_FORMAT_DOUBLE: - /* Only float and double need a special case. */ - ss->format = PA_SAMPLE_FLOAT32NE; - readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float; - break; - default: - /* Everything else is cleanly converted to signed 16 bit. */ - ss->format = PA_SAMPLE_S16NE; - readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short; - break; - } - - ss->rate = sfinfo.samplerate; - ss->channels = sfinfo.channels; - - if (!pa_sample_spec_valid(ss)) { - pa_log(__FILE__": Unsupported sample format in file %s\n", fname); - goto finish; - } - - if ((l = pa_frame_size(ss)*sfinfo.frames) > MAX_FILE_SIZE) { - pa_log(__FILE__": File too large\n"); - goto finish; - } - - chunk->memblock = pa_memblock_new(l, s); - assert(chunk->memblock); - chunk->index = 0; - chunk->length = l; - - if (readf_function(sf, chunk->memblock->data, sfinfo.frames) != sfinfo.frames) { - pa_log(__FILE__": Premature file end\n"); - goto finish; - } - - ret = 0; - -finish: - - if (sf) - sf_close(sf); - - if (ret != 0 && chunk->memblock) - pa_memblock_unref(chunk->memblock); - - return ret; - -} - -int pa_sound_file_too_big_to_cache(const char *fname) { - SNDFILE*sf = NULL; - SF_INFO sfinfo; - pa_sample_spec ss; - - if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) { - pa_log(__FILE__": Failed to open file %s\n", fname); - return 0; - } - - sf_close(sf); - - switch (sfinfo.format & SF_FORMAT_SUBMASK) { - case SF_FORMAT_FLOAT: - case SF_FORMAT_DOUBLE: - /* Only float and double need a special case. */ - ss.format = PA_SAMPLE_FLOAT32NE; - break; - default: - /* Everything else is cleanly converted to signed 16 bit. */ - ss.format = PA_SAMPLE_S16NE; - break; - } - - ss.rate = sfinfo.samplerate; - ss.channels = sfinfo.channels; - - if ((pa_frame_size(&ss) * sfinfo.frames) > MAX_FILE_SIZE) { - pa_log(__FILE__": File too large %s\n", fname); - return 1; - } - - return 0; -} diff --git a/polyp/sound-file.h b/polyp/sound-file.h deleted file mode 100644 index e0534275..00000000 --- a/polyp/sound-file.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef soundfilehfoo -#define soundfilehfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "memchunk.h" -#include "sample.h" - -int pa_sound_file_load(const char *fname, pa_sample_spec *ss, pa_memchunk *chunk, pa_memblock_stat *s); - -int pa_sound_file_too_big_to_cache(const char *fname); - -#endif diff --git a/polyp/source-output.c b/polyp/source-output.c deleted file mode 100644 index e1d8ccf7..00000000 --- a/polyp/source-output.c +++ /dev/null @@ -1,228 +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 -#endif - -#include -#include -#include -#include - -#include "source-output.h" -#include "xmalloc.h" -#include "subscribe.h" -#include "log.h" - -pa_source_output* pa_source_output_new( - pa_source *s, - const char *driver, - const char *name, - const pa_sample_spec *spec, - const pa_channel_map *map, - int resample_method) { - - pa_source_output *o; - pa_resampler *resampler = NULL; - int r; - char st[256]; - pa_channel_map tmap; - - assert(s); - assert(spec); - assert(s->state == PA_SOURCE_RUNNING); - - if (pa_idxset_size(s->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) { - pa_log(__FILE__": Failed to create source output: too many outputs per source.\n"); - return NULL; - } - - if (resample_method == PA_RESAMPLER_INVALID) - resample_method = s->core->resample_method; - - if (!map) { - pa_channel_map_init_auto(&tmap, spec->channels); - map = &tmap; - } - - if (!pa_sample_spec_equal(&s->sample_spec, spec) || !pa_channel_map_equal(&s->channel_map, map)) - if (!(resampler = pa_resampler_new(&s->sample_spec, &s->channel_map, spec, map, s->core->memblock_stat, resample_method))) - return NULL; - - o = pa_xmalloc(sizeof(pa_source_output)); - o->ref = 1; - o->state = PA_SOURCE_OUTPUT_RUNNING; - o->name = pa_xstrdup(name); - o->driver = pa_xstrdup(driver); - o->owner = NULL; - o->source = s; - o->client = NULL; - - o->sample_spec = *spec; - o->channel_map = *map; - - o->push = NULL; - o->kill = NULL; - o->get_latency = NULL; - o->userdata = NULL; - - o->resampler = resampler; - - assert(s->core); - r = pa_idxset_put(s->core->source_outputs, o, &o->index); - assert(r == 0 && o->index != PA_IDXSET_INVALID); - r = pa_idxset_put(s->outputs, o, NULL); - assert(r == 0); - - pa_sample_spec_snprint(st, sizeof(st), spec); - pa_log_info(__FILE__": created %u \"%s\" on %u with sample spec \"%s\"\n", o->index, o->name, s->index, st); - - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index); - - return o; -} - -void pa_source_output_disconnect(pa_source_output*o) { - assert(o); - assert(o->state != PA_SOURCE_OUTPUT_DISCONNECTED); - assert(o->source); - assert(o->source->core); - - pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL); - pa_idxset_remove_by_data(o->source->outputs, o, NULL); - - pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index); - o->source = NULL; - - o->push = NULL; - o->kill = NULL; - o->get_latency = NULL; - - o->state = PA_SOURCE_OUTPUT_DISCONNECTED; -} - -static void source_output_free(pa_source_output* o) { - assert(o); - - if (o->state != PA_SOURCE_OUTPUT_DISCONNECTED) - pa_source_output_disconnect(o); - - pa_log_info(__FILE__": freed %u \"%s\"\n", o->index, o->name); - - if (o->resampler) - pa_resampler_free(o->resampler); - - pa_xfree(o->name); - pa_xfree(o->driver); - pa_xfree(o); -} - - -void pa_source_output_unref(pa_source_output* o) { - assert(o); - assert(o->ref >= 1); - - if (!(--o->ref)) - source_output_free(o); -} - -pa_source_output* pa_source_output_ref(pa_source_output *o) { - assert(o); - assert(o->ref >= 1); - - o->ref++; - return o; -} - - -void pa_source_output_kill(pa_source_output*o) { - assert(o); - assert(o->ref >= 1); - - if (o->kill) - o->kill(o); -} - -void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { - pa_memchunk rchunk; - - assert(o); - assert(chunk); - assert(chunk->length); - assert(o->push); - - if (o->state == PA_SOURCE_OUTPUT_CORKED) - return; - - if (!o->resampler) { - o->push(o, chunk); - return; - } - - pa_resampler_run(o->resampler, chunk, &rchunk); - if (!rchunk.length) - return; - - assert(rchunk.memblock); - o->push(o, &rchunk); - pa_memblock_unref(rchunk.memblock); -} - -void pa_source_output_set_name(pa_source_output *o, const char *name) { - assert(o); - assert(o->ref >= 1); - - pa_xfree(o->name); - o->name = pa_xstrdup(name); - - pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); -} - -pa_usec_t pa_source_output_get_latency(pa_source_output *o) { - assert(o); - assert(o->ref >= 1); - - if (o->get_latency) - return o->get_latency(o); - - return 0; -} - -void pa_source_output_cork(pa_source_output *o, int b) { - assert(o); - assert(o->ref >= 1); - - if (o->state == PA_SOURCE_OUTPUT_DISCONNECTED) - return; - - o->state = b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING; -} - -pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) { - assert(o); - assert(o->ref >= 1); - - if (!o->resampler) - return PA_RESAMPLER_INVALID; - - return pa_resampler_get_method(o->resampler); -} diff --git a/polyp/source-output.h b/polyp/source-output.h deleted file mode 100644 index f8e4b152..00000000 --- a/polyp/source-output.h +++ /dev/null @@ -1,92 +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 - -typedef struct pa_source_output pa_source_output; - -#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; - pa_module *owner; - - pa_source *source; - pa_client *client; - - pa_sample_spec sample_spec; - pa_channel_map channel_map; - - void (*push)(pa_source_output *o, const pa_memchunk *chunk); - void (*kill)(pa_source_output* o); - pa_usec_t (*get_latency) (pa_source_output *i); - - pa_resampler* resampler; - - void *userdata; -}; - -pa_source_output* pa_source_output_new( - pa_source *s, - const char *driver, - const char *name, - const pa_sample_spec *spec, - const pa_channel_map *map, - int resample_method); - -void pa_source_output_unref(pa_source_output* o); -pa_source_output* pa_source_output_ref(pa_source_output *o); - -/* To be called by the implementing module only */ -void pa_source_output_disconnect(pa_source_output*o); - -/* External code may request disconnection with this funcion */ -void pa_source_output_kill(pa_source_output*o); - -void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk); - -void pa_source_output_set_name(pa_source_output *i, const char *name); - -pa_usec_t pa_source_output_get_latency(pa_source_output *i); - -void pa_source_output_cork(pa_source_output *i, int b); - -pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o); - -#endif diff --git a/polyp/source.c b/polyp/source.c deleted file mode 100644 index 6e377b20..00000000 --- a/polyp/source.c +++ /dev/null @@ -1,195 +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 -#endif - -#include -#include -#include -#include - -#include "source.h" -#include "source-output.h" -#include "namereg.h" -#include "xmalloc.h" -#include "subscribe.h" -#include "log.h" - -pa_source* pa_source_new( - pa_core *core, - const char *driver, - const char *name, - int fail, - const pa_sample_spec *spec, - const pa_channel_map *map) { - - pa_source *s; - char st[256]; - int r; - - assert(core); - assert(name); - assert(*name); - assert(spec); - - s = pa_xnew(pa_source, 1); - - if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SOURCE, s, fail))) { - pa_xfree(s); - return NULL; - } - - s->ref = 1; - s->core = core; - s->state = PA_SOURCE_RUNNING; - s->name = pa_xstrdup(name); - s->description = NULL; - s->driver = pa_xstrdup(driver); - s->owner = NULL; - - s->sample_spec = *spec; - if (map) - s->channel_map = *map; - else - pa_channel_map_init_auto(&s->channel_map, spec->channels); - - s->outputs = pa_idxset_new(NULL, NULL); - s->monitor_of = NULL; - - s->get_latency = NULL; - s->notify = NULL; - s->userdata = NULL; - - r = pa_idxset_put(core->sources, s, &s->index); - assert(s->index != PA_IDXSET_INVALID && r >= 0); - - pa_sample_spec_snprint(st, sizeof(st), spec); - pa_log_info(__FILE__": created %u \"%s\" with sample spec \"%s\"\n", s->index, s->name, st); - - pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index); - - return s; -} - -void pa_source_disconnect(pa_source *s) { - pa_source_output *o, *j = NULL; - - assert(s); - assert(s->state == PA_SOURCE_RUNNING); - - pa_namereg_unregister(s->core, s->name); - - while ((o = pa_idxset_first(s->outputs, NULL))) { - assert(o != j); - pa_source_output_kill(o); - j = o; - } - - pa_idxset_remove_by_data(s->core->sources, s, NULL); - - s->get_latency = NULL; - s->notify = NULL; - - s->state = PA_SOURCE_DISCONNECTED; - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); -} - -static void source_free(pa_source *s) { - assert(s); - assert(!s->ref); - - if (s->state != PA_SOURCE_DISCONNECTED) - pa_source_disconnect(s); - - pa_log_info(__FILE__": freed %u \"%s\"\n", s->index, s->name); - - pa_idxset_free(s->outputs, NULL, NULL); - - pa_xfree(s->name); - pa_xfree(s->description); - pa_xfree(s->driver); - pa_xfree(s); -} - -void pa_source_unref(pa_source *s) { - assert(s); - assert(s->ref >= 1); - - if (!(--s->ref)) - source_free(s); -} - -pa_source* pa_source_ref(pa_source *s) { - assert(s); - assert(s->ref >= 1); - - s->ref++; - return s; -} - -void pa_source_notify(pa_source*s) { - assert(s); - assert(s->ref >= 1); - - if (s->notify) - s->notify(s); -} - -static int do_post(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void*userdata) { - pa_source_output *o = p; - const pa_memchunk *chunk = userdata; - - assert(o); - assert(chunk); - - pa_source_output_push(o, chunk); - return 0; -} - -void pa_source_post(pa_source*s, const pa_memchunk *chunk) { - assert(s); - assert(s->ref >= 1); - assert(chunk); - - pa_source_ref(s); - pa_idxset_foreach(s->outputs, do_post, (void*) chunk); - pa_source_unref(s); -} - -void pa_source_set_owner(pa_source *s, pa_module *m) { - assert(s); - assert(s->ref >= 1); - - s->owner = m; -} - -pa_usec_t pa_source_get_latency(pa_source *s) { - assert(s); - assert(s->ref >= 1); - - if (!s->get_latency) - return 0; - - return s->get_latency(s); -} - diff --git a/polyp/source.h b/polyp/source.h deleted file mode 100644 index 823a9424..00000000 --- a/polyp/source.h +++ /dev/null @@ -1,86 +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. -***/ - -typedef struct pa_source pa_source; - -#include -#include "core.h" -#include "sample.h" -#include "idxset.h" -#include "memblock.h" -#include "memchunk.h" -#include "sink.h" -#include "channelmap.h" -#include "module.h" - -#define PA_MAX_OUTPUTS_PER_SOURCE 16 - -typedef enum pa_source_state { - PA_SOURCE_RUNNING, - PA_SOURCE_DISCONNECTED -} pa_source_state_t; - -struct pa_source { - int ref; - uint32_t index; - pa_core *core; - pa_source_state_t state; - - char *name, *description, *driver; - pa_module *owner; - - pa_sample_spec sample_spec; - pa_channel_map channel_map; - - pa_idxset *outputs; - pa_sink *monitor_of; - - void (*notify)(pa_source*source); - pa_usec_t (*get_latency)(pa_source *s); - - void *userdata; -}; - -pa_source* pa_source_new( - pa_core *core, - const char *driver, - const char *name, - int namereg_fail, - const pa_sample_spec *spec, - const pa_channel_map *map); - -void pa_source_disconnect(pa_source *s); -void pa_source_unref(pa_source *s); -pa_source* pa_source_ref(pa_source *c); - -/* Pass a new memory block to all output streams */ -void pa_source_post(pa_source*s, const pa_memchunk *b); - -void pa_source_notify(pa_source *s); - -void pa_source_set_owner(pa_source *s, pa_module *m); - -pa_usec_t pa_source_get_latency(pa_source *s); - -#endif diff --git a/polyp/strbuf.c b/polyp/strbuf.c deleted file mode 100644 index aa20218a..00000000 --- a/polyp/strbuf.c +++ /dev/null @@ -1,166 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "strbuf.h" - -/* A chunk of the linked list that makes up the string */ -struct chunk { - struct chunk *next; - size_t length; -}; - -#define CHUNK_TO_TEXT(c) ((char*) (c) + sizeof(struct chunk)) - -struct pa_strbuf { - size_t length; - struct chunk *head, *tail; -}; - -pa_strbuf *pa_strbuf_new(void) { - pa_strbuf *sb = pa_xmalloc(sizeof(pa_strbuf)); - sb->length = 0; - sb->head = sb->tail = NULL; - return sb; -} - -void pa_strbuf_free(pa_strbuf *sb) { - assert(sb); - while (sb->head) { - struct chunk *c = sb->head; - sb->head = sb->head->next; - pa_xfree(c); - } - - pa_xfree(sb); -} - -/* Make a C string from the string buffer. The caller has to free - * string with pa_xfree(). */ -char *pa_strbuf_tostring(pa_strbuf *sb) { - char *t, *e; - struct chunk *c; - assert(sb); - - e = t = pa_xmalloc(sb->length+1); - - for (c = sb->head; c; c = c->next) { - assert((size_t) (e-t) <= sb->length); - memcpy(e, CHUNK_TO_TEXT(c), c->length); - e += c->length; - } - - /* Trailing NUL */ - *e = 0; - - assert(e == t+sb->length); - - return t; -} - -/* Combination of pa_strbuf_free() and pa_strbuf_tostring() */ -char *pa_strbuf_tostring_free(pa_strbuf *sb) { - char *t; - assert(sb); - t = pa_strbuf_tostring(sb); - pa_strbuf_free(sb); - return t; -} - -/* Append a string to the string buffer */ -void pa_strbuf_puts(pa_strbuf *sb, const char *t) { - assert(sb && t); - pa_strbuf_putsn(sb, t, strlen(t)); -} - -/* Append a new chunk to the linked list */ -static void append(pa_strbuf *sb, struct chunk *c) { - assert(sb && c); - - if (sb->tail) { - assert(sb->head); - sb->tail->next = c; - } else { - assert(!sb->head); - sb->head = c; - } - - sb->tail = c; - sb->length += c->length; - c->next = NULL; -} - -/* Append up to l bytes of a string to the string buffer */ -void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) { - struct chunk *c; - assert(sb && t); - - if (!l) - return; - - c = pa_xmalloc(sizeof(struct chunk)+l); - c->length = l; - memcpy(CHUNK_TO_TEXT(c), t, l); - - append(sb, c); -} - -/* Append a printf() style formatted string to the string buffer. */ -/* The following is based on an example from the GNU libc documentation */ -int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) { - int size = 100; - struct chunk *c = NULL; - - assert(sb); - - for(;;) { - va_list ap; - int r; - - c = pa_xrealloc(c, sizeof(struct chunk)+size); - - va_start(ap, format); - r = vsnprintf(CHUNK_TO_TEXT(c), size, format, ap); - va_end(ap); - - if (r > -1 && r < size) { - c->length = r; - append(sb, c); - return r; - } - - if (r > -1) /* glibc 2.1 */ - size = r+1; - else /* glibc 2.0 */ - size *= 2; - } -} diff --git a/polyp/strbuf.h b/polyp/strbuf.h deleted file mode 100644 index a88f5190..00000000 --- a/polyp/strbuf.h +++ /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 "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/polyp/strlist-test.c b/polyp/strlist-test.c deleted file mode 100644 index c670a105..00000000 --- a/polyp/strlist-test.c +++ /dev/null @@ -1,42 +0,0 @@ -#include - -#include "strlist.h" -#include "xmalloc.h" -#include "gccmacro.h" - -int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char* argv[]) { - char *t, *u; - pa_strlist *l = NULL; - - l = pa_strlist_prepend(l, "e"); - l = pa_strlist_prepend(l, "d"); - l = pa_strlist_prepend(l, "c"); - l = pa_strlist_prepend(l, "b"); - l = pa_strlist_prepend(l, "a"); - - t = pa_strlist_tostring(l); - pa_strlist_free(l); - - fprintf(stderr, "1: %s\n", t); - - l = pa_strlist_parse(t); - pa_xfree(t); - - t = pa_strlist_tostring(l); - fprintf(stderr, "2: %s\n", t); - pa_xfree(t); - - l = pa_strlist_pop(l, &u); - fprintf(stderr, "3: %s\n", u); - pa_xfree(u); - - l = pa_strlist_remove(l, "c"); - - t = pa_strlist_tostring(l); - fprintf(stderr, "4: %s\n", t); - pa_xfree(t); - - pa_strlist_free(l); - - return 0; -} diff --git a/polyp/strlist.c b/polyp/strlist.c deleted file mode 100644 index 09eb0c8a..00000000 --- a/polyp/strlist.c +++ /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 -#endif - -#include -#include - -#include "strlist.h" -#include "xmalloc.h" -#include "strbuf.h" -#include "util.h" - -struct pa_strlist { - pa_strlist *next; - char *str; -}; - -pa_strlist* pa_strlist_prepend(pa_strlist *l, const char *s) { - pa_strlist *n; - assert(s); - n = pa_xmalloc(sizeof(pa_strlist)); - n->str = pa_xstrdup(s); - n->next = l; - return n; -} - -char *pa_strlist_tostring(pa_strlist *l) { - int first = 1; - pa_strbuf *b; - - b = pa_strbuf_new(); - for (; l; l = l->next) { - if (!first) - pa_strbuf_puts(b, " "); - first = 0; - pa_strbuf_puts(b, l->str); - } - - return pa_strbuf_tostring_free(b); -} - -pa_strlist* pa_strlist_remove(pa_strlist *l, const char *s) { - pa_strlist *ret = l, *prev = NULL; - assert(l && s); - - while (l) { - if (!strcmp(l->str, s)) { - pa_strlist *n = l->next; - - if (!prev) { - assert(ret == l); - ret = n; - } else - prev->next = n; - - pa_xfree(l->str); - pa_xfree(l); - - l = n; - - } else { - prev = l; - l = l->next; - } - } - - return ret; -} - -void pa_strlist_free(pa_strlist *l) { - while (l) { - pa_strlist *c = l; - l = l->next; - - pa_xfree(c->str); - pa_xfree(c); - } -} - -pa_strlist* pa_strlist_pop(pa_strlist *l, char **s) { - pa_strlist *r; - assert(s); - - if (!l) { - *s = NULL; - return NULL; - } - - *s = l->str; - r = l->next; - pa_xfree(l); - return r; -} - -pa_strlist* pa_strlist_parse(const char *s) { - pa_strlist *head = NULL, *p = NULL; - const char *state = NULL; - char *r; - - while ((r = pa_split_spaces(s, &state))) { - pa_strlist *n; - - n = pa_xmalloc(sizeof(pa_strlist)); - n->str = r; - n->next = NULL; - - if (p) - p->next = n; - else - head = n; - - p = n; - } - - return head; -} diff --git a/polyp/strlist.h b/polyp/strlist.h deleted file mode 100644 index 2c54dc74..00000000 --- a/polyp/strlist.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef foostrlisthfoo -#define foostrlisthfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -typedef struct pa_strlist pa_strlist; - -/* Add the specified server string to the list, return the new linked list head */ -pa_strlist* pa_strlist_prepend(pa_strlist *l, const char *s); - -/* Remove the specified string from the list, return the new linked list head */ -pa_strlist* pa_strlist_remove(pa_strlist *l, const char *s); - -/* Make a whitespace separated string of all server stringes. Returned memory has to be freed with pa_xfree() */ -char *pa_strlist_tostring(pa_strlist *l); - -/* Free the entire list */ -void pa_strlist_free(pa_strlist *l); - -/* Return the next entry in the list in *string and remove it from - * the list. Returns the new list head. The memory *string points to - * has to be freed with pa_xfree() */ -pa_strlist* pa_strlist_pop(pa_strlist *l, char **s); - -/* Parse a whitespace separated server list */ -pa_strlist* pa_strlist_parse(const char *s); - -#endif diff --git a/polyp/subscribe.c b/polyp/subscribe.c deleted file mode 100644 index e8b3c841..00000000 --- a/polyp/subscribe.c +++ /dev/null @@ -1,230 +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 -#endif - -#include -#include - -#include "queue.h" -#include "subscribe.h" -#include "xmalloc.h" -#include "log.h" - -/* The subscription subsystem may be used to be notified whenever an - * entity (sink, source, ...) is created or deleted. Modules may - * register a callback function that is called whenever an event - * matching a subscription mask happens. The execution of the callback - * function is postponed to the next main loop iteration, i.e. is not - * called from within the stack frame the entity was created in. */ - -struct pa_subscription { - pa_core *core; - int dead; - void (*callback)(pa_core *c, pa_subscription_event_type_t t, uint32_t index, void *userdata); - void *userdata; - pa_subscription_mask_t mask; - - pa_subscription *prev, *next; -}; - -struct pa_subscription_event { - pa_subscription_event_type_t type; - uint32_t index; -}; - -static void sched_event(pa_core *c); - -/* Allocate a new subscription object for the given subscription mask. Use the specified callback function and user data */ -pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, void (*callback)(pa_core *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata) { - pa_subscription *s; - assert(c); - - s = pa_xmalloc(sizeof(pa_subscription)); - s->core = c; - s->dead = 0; - s->callback = callback; - s->userdata = userdata; - s->mask = m; - - if ((s->next = c->subscriptions)) - s->next->prev = s; - s->prev = NULL; - c->subscriptions = s; - return s; -} - -/* Free a subscription object, effectively marking it for deletion */ -void pa_subscription_free(pa_subscription*s) { - assert(s && !s->dead); - s->dead = 1; - sched_event(s->core); -} - -static void free_item(pa_subscription *s) { - assert(s && s->core); - - if (s->prev) - s->prev->next = s->next; - else - s->core->subscriptions = s->next; - - if (s->next) - s->next->prev = s->prev; - - pa_xfree(s); -} - -/* Free all subscription objects */ -void pa_subscription_free_all(pa_core *c) { - pa_subscription_event *e; - assert(c); - - while (c->subscriptions) - free_item(c->subscriptions); - - if (c->subscription_event_queue) { - while ((e = pa_queue_pop(c->subscription_event_queue))) - pa_xfree(e); - - pa_queue_free(c->subscription_event_queue, NULL, NULL); - c->subscription_event_queue = NULL; - } - - if (c->subscription_defer_event) { - c->mainloop->defer_free(c->subscription_defer_event); - c->subscription_defer_event = NULL; - } -} - -/*static void dump_event(pa_subscription_event*e) { - switch (e->type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { - case PA_SUBSCRIPTION_EVENT_SINK: - pa_log(__FILE__": SINK_EVENT"); - break; - case PA_SUBSCRIPTION_EVENT_SOURCE: - pa_log(__FILE__": SOURCE_EVENT"); - break; - case PA_SUBSCRIPTION_EVENT_SINK_INPUT: - pa_log(__FILE__": SINK_INPUT_EVENT"); - break; - case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: - pa_log(__FILE__": SOURCE_OUTPUT_EVENT"); - break; - case PA_SUBSCRIPTION_EVENT_MODULE: - pa_log(__FILE__": MODULE_EVENT"); - break; - case PA_SUBSCRIPTION_EVENT_CLIENT: - pa_log(__FILE__": CLIENT_EVENT"); - break; - default: - pa_log(__FILE__": OTHER"); - break; - } - - switch (e->type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) { - case PA_SUBSCRIPTION_EVENT_NEW: - pa_log(__FILE__": NEW"); - break; - case PA_SUBSCRIPTION_EVENT_CHANGE: - pa_log(__FILE__": CHANGE"); - break; - case PA_SUBSCRIPTION_EVENT_REMOVE: - pa_log(__FILE__": REMOVE"); - break; - default: - pa_log(__FILE__": OTHER"); - break; - } - - pa_log(__FILE__": %u\n", e->index); -}*/ - -/* Deferred callback for dispatching subscirption events */ -static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) { - pa_core *c = userdata; - pa_subscription *s; - assert(c && c->subscription_defer_event == de && c->mainloop == m); - - c->mainloop->defer_enable(c->subscription_defer_event, 0); - - - /* Dispatch queued events */ - - if (c->subscription_event_queue) { - pa_subscription_event *e; - - while ((e = pa_queue_pop(c->subscription_event_queue))) { - - for (s = c->subscriptions; s; s = s->next) { - - if (!s->dead && pa_subscription_match_flags(s->mask, e->type)) - s->callback(c, e->type, e->index, s->userdata); - } - - pa_xfree(e); - } - } - - /* Remove dead subscriptions */ - - s = c->subscriptions; - while (s) { - pa_subscription *n = s->next; - if (s->dead) - free_item(s); - s = n; - } -} - -/* Schedule an mainloop event so that a pending subscription event is dispatched */ -static void sched_event(pa_core *c) { - assert(c); - - if (!c->subscription_defer_event) { - c->subscription_defer_event = c->mainloop->defer_new(c->mainloop, defer_cb, c); - assert(c->subscription_defer_event); - } - - c->mainloop->defer_enable(c->subscription_defer_event, 1); -} - -/* Append a new subscription event to the subscription event queue and schedule a main loop event */ -void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t index) { - pa_subscription_event *e; - assert(c); - - e = pa_xmalloc(sizeof(pa_subscription_event)); - e->type = t; - e->index = index; - - if (!c->subscription_event_queue) { - c->subscription_event_queue = pa_queue_new(); - assert(c->subscription_event_queue); - } - - pa_queue_push(c->subscription_event_queue, e); - sched_event(c); -} - - diff --git a/polyp/subscribe.h b/polyp/subscribe.h deleted file mode 100644 index 625159e3..00000000 --- a/polyp/subscribe.h +++ /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. -***/ - -typedef struct pa_subscription pa_subscription; -typedef struct pa_subscription_event pa_subscription_event; - -#include "core.h" -#include "native-common.h" - -pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, void (*callback)(pa_core *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata); -void pa_subscription_free(pa_subscription*s); -void pa_subscription_free_all(pa_core *c); - -void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t idx); - -#endif diff --git a/polyp/tagstruct.c b/polyp/tagstruct.c deleted file mode 100644 index 676f67de..00000000 --- a/polyp/tagstruct.c +++ /dev/null @@ -1,609 +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 -#endif - -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_NETINET_IN_H -#include -#endif - -#include "winsock.h" - -#include "tagstruct.h" -#include "xmalloc.h" - - -struct pa_tagstruct { - uint8_t *data; - size_t length, allocated; - size_t rindex; - - int dynamic; -}; - -pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) { - pa_tagstruct*t; - - assert(!data || (data && length)); - - t = pa_xmalloc(sizeof(pa_tagstruct)); - t->data = (uint8_t*) data; - t->allocated = t->length = data ? length : 0; - t->rindex = 0; - t->dynamic = !data; - return t; -} - -void pa_tagstruct_free(pa_tagstruct*t) { - assert(t); - if (t->dynamic) - pa_xfree(t->data); - pa_xfree(t); -} - -uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l) { - uint8_t *p; - assert(t && t->dynamic && l); - p = t->data; - *l = t->length; - pa_xfree(t); - return p; -} - -static void extend(pa_tagstruct*t, size_t l) { - assert(t && t->dynamic); - - if (t->length+l <= t->allocated) - return; - - t->data = pa_xrealloc(t->data, t->allocated = t->length+l+100); -} - - -void pa_tagstruct_puts(pa_tagstruct*t, const char *s) { - size_t l; - assert(t); - if (s) { - l = strlen(s)+2; - extend(t, l); - t->data[t->length] = PA_TAG_STRING; - strcpy((char*) (t->data+t->length+1), s); - t->length += l; - } else { - extend(t, 1); - t->data[t->length] = PA_TAG_STRING_NULL; - t->length += 1; - } -} - -void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) { - assert(t); - extend(t, 5); - t->data[t->length] = PA_TAG_U32; - i = htonl(i); - memcpy(t->data+t->length+1, &i, 4); - t->length += 5; -} - -void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) { - assert(t); - extend(t, 2); - t->data[t->length] = PA_TAG_U8; - *(t->data+t->length+1) = c; - t->length += 2; -} - -void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) { - uint32_t rate; - assert(t && ss); - extend(t, 7); - t->data[t->length] = PA_TAG_SAMPLE_SPEC; - t->data[t->length+1] = (uint8_t) ss->format; - t->data[t->length+2] = ss->channels; - rate = htonl(ss->rate); - memcpy(t->data+t->length+3, &rate, 4); - t->length += 7; -} - -void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) { - uint32_t tmp; - assert(t && p); - - extend(t, 5+length); - t->data[t->length] = PA_TAG_ARBITRARY; - tmp = htonl(length); - memcpy(t->data+t->length+1, &tmp, 4); - if (length) - memcpy(t->data+t->length+5, p, length); - t->length += 5+length; -} - -void pa_tagstruct_put_boolean(pa_tagstruct*t, int b) { - assert(t); - extend(t, 1); - t->data[t->length] = b ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE; - t->length += 1; -} - -void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) { - uint32_t tmp; - assert(t); - extend(t, 9); - t->data[t->length] = PA_TAG_TIMEVAL; - tmp = htonl(tv->tv_sec); - memcpy(t->data+t->length+1, &tmp, 4); - tmp = htonl(tv->tv_usec); - memcpy(t->data+t->length+5, &tmp, 4); - t->length += 9; -} - -void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) { - uint32_t tmp; - assert(t); - extend(t, 9); - t->data[t->length] = PA_TAG_USEC; - tmp = htonl((uint32_t) (u >> 32)); - memcpy(t->data+t->length+1, &tmp, 4); - tmp = htonl((uint32_t) u); - memcpy(t->data+t->length+5, &tmp, 4); - t->length += 9; -} - -void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) { - uint32_t tmp; - assert(t); - extend(t, 9); - t->data[t->length] = PA_TAG_U64; - tmp = htonl((uint32_t) (u >> 32)); - memcpy(t->data+t->length+1, &tmp, 4); - tmp = htonl((uint32_t) u); - memcpy(t->data+t->length+5, &tmp, 4); - t->length += 9; -} - -void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map) { - unsigned i; - - assert(t); - extend(t, 2 + map->channels); - - t->data[t->length++] = PA_TAG_CHANNEL_MAP; - t->data[t->length++] = map->channels; - - for (i = 0; i < map->channels; i ++) - t->data[t->length++] = (uint8_t) map->map[i]; -} - -void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) { - unsigned i; - pa_volume_t vol; - - assert(t); - extend(t, 2 + cvolume->channels * sizeof(pa_volume_t)); - - t->data[t->length++] = PA_TAG_CVOLUME; - t->data[t->length++] = cvolume->channels; - - for (i = 0; i < cvolume->channels; i ++) { - vol = htonl(cvolume->values[i]); - memcpy(t->data + t->length, &vol, sizeof(pa_volume_t)); - t->length += sizeof(pa_volume_t); - } -} - -int pa_tagstruct_gets(pa_tagstruct*t, const char **s) { - int error = 0; - size_t n; - char *c; - assert(t && s); - - if (t->rindex+1 > t->length) - return -1; - - if (t->data[t->rindex] == PA_TAG_STRING_NULL) { - t->rindex++; - *s = NULL; - return 0; - } - - if (t->rindex+2 > t->length) - return -1; - - if (t->data[t->rindex] != PA_TAG_STRING) - return -1; - - error = 1; - for (n = 0, c = (char*) (t->data+t->rindex+1); t->rindex+1+n < t->length; n++, c++) - if (!*c) { - error = 0; - break; - } - - if (error) - return -1; - - *s = (char*) (t->data+t->rindex+1); - - t->rindex += n+2; - return 0; -} - -int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i) { - assert(t && i); - - if (t->rindex+5 > t->length) - return -1; - - if (t->data[t->rindex] != PA_TAG_U32) - return -1; - - memcpy(i, t->data+t->rindex+1, 4); - *i = ntohl(*i); - t->rindex += 5; - return 0; -} - -int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c) { - assert(t && c); - - if (t->rindex+2 > t->length) - return -1; - - if (t->data[t->rindex] != PA_TAG_U8) - return -1; - - *c = t->data[t->rindex+1]; - t->rindex +=2; - return 0; -} - -int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss) { - assert(t && ss); - - if (t->rindex+7 > t->length) - return -1; - - if (t->data[t->rindex] != PA_TAG_SAMPLE_SPEC) - return -1; - - ss->format = t->data[t->rindex+1]; - ss->channels = t->data[t->rindex+2]; - memcpy(&ss->rate, t->data+t->rindex+3, 4); - ss->rate = ntohl(ss->rate); - - if (!pa_sample_spec_valid(ss)) - return -1; - - t->rindex += 7; - return 0; -} - -int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) { - uint32_t len; - assert(t && p); - - if (t->rindex+5+length > t->length) - return -1; - - if (t->data[t->rindex] != PA_TAG_ARBITRARY) - return -1; - - memcpy(&len, t->data+t->rindex+1, 4); - if (ntohl(len) != length) - return -1; - - *p = t->data+t->rindex+5; - t->rindex += 5+length; - return 0; -} - -int pa_tagstruct_eof(pa_tagstruct*t) { - assert(t); - return t->rindex >= t->length; -} - -const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) { - assert(t && t->dynamic && l); - *l = t->length; - return t->data; -} - -int pa_tagstruct_get_boolean(pa_tagstruct*t, int *b) { - assert(t && b); - - if (t->rindex+1 > t->length) - return -1; - - if (t->data[t->rindex] == PA_TAG_BOOLEAN_TRUE) - *b = 1; - else if (t->data[t->rindex] == PA_TAG_BOOLEAN_FALSE) - *b = 0; - else - return -1; - - t->rindex +=1; - return 0; -} - -int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) { - - if (t->rindex+9 > t->length) - return -1; - - if (t->data[t->rindex] != PA_TAG_TIMEVAL) - return -1; - - memcpy(&tv->tv_sec, t->data+t->rindex+1, 4); - tv->tv_sec = ntohl(tv->tv_sec); - memcpy(&tv->tv_usec, t->data+t->rindex+5, 4); - tv->tv_usec = ntohl(tv->tv_usec); - t->rindex += 9; - return 0; - -} - -int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u) { - uint32_t tmp; - assert(t && u); - - if (t->rindex+9 > t->length) - return -1; - - if (t->data[t->rindex] != PA_TAG_USEC) - return -1; - - memcpy(&tmp, t->data+t->rindex+1, 4); - *u = (pa_usec_t) ntohl(tmp) << 32; - memcpy(&tmp, t->data+t->rindex+5, 4); - *u |= (pa_usec_t) ntohl(tmp); - t->rindex +=9; - return 0; -} - -int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) { - uint32_t tmp; - assert(t && u); - - if (t->rindex+9 > t->length) - return -1; - - if (t->data[t->rindex] != PA_TAG_U64) - return -1; - - memcpy(&tmp, t->data+t->rindex+1, 4); - *u = (pa_usec_t) ntohl(tmp) << 32; - memcpy(&tmp, t->data+t->rindex+5, 4); - *u |= (pa_usec_t) ntohl(tmp); - t->rindex +=9; - return 0; -} - -int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map) { - unsigned i; - - assert(t); - assert(map); - - if (t->rindex+2 > t->length) - return -1; - - if (t->data[t->rindex] != PA_TAG_CHANNEL_MAP) - return -1; - - if ((map->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX) - return -1; - - if (t->rindex+2+map->channels > t->length) - return -1; - - for (i = 0; i < map->channels; i ++) - map->map[i] = (int8_t) t->data[t->rindex + 2 + i]; - - if (!pa_channel_map_valid(map)) - return -1; - - t->rindex += 2 + map->channels; - return 0; -} - -int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) { - unsigned i; - pa_volume_t vol; - - assert(t); - assert(cvolume); - - if (t->rindex+2 > t->length) - return -1; - - if (t->data[t->rindex] != PA_TAG_CVOLUME) - return -1; - - if ((cvolume->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX) - return -1; - - if (t->rindex+2+cvolume->channels*sizeof(pa_volume_t) > t->length) - return -1; - - for (i = 0; i < cvolume->channels; i ++) { - memcpy(&vol, t->data + t->rindex + 2 + i * sizeof(pa_volume_t), sizeof(pa_volume_t)); - cvolume->values[i] = (pa_volume_t) ntohl(vol); - } - - if (!pa_cvolume_valid(cvolume)) - return -1; - - t->rindex += 2 + cvolume->channels * sizeof(pa_volume_t); - return 0; -} - -void pa_tagstruct_put(pa_tagstruct *t, ...) { - va_list va; - assert(t); - - va_start(va, t); - - for (;;) { - int tag = va_arg(va, int); - - if (tag == PA_TAG_INVALID) - break; - - switch (tag) { - case PA_TAG_STRING: - case PA_TAG_STRING_NULL: - pa_tagstruct_puts(t, va_arg(va, char*)); - break; - - case PA_TAG_U32: - pa_tagstruct_putu32(t, va_arg(va, uint32_t)); - break; - - case PA_TAG_U8: - pa_tagstruct_putu8(t, (uint8_t) va_arg(va, int)); - break; - - case PA_TAG_U64: - pa_tagstruct_putu64(t, va_arg(va, uint64_t)); - break; - - case PA_TAG_SAMPLE_SPEC: - pa_tagstruct_put_sample_spec(t, va_arg(va, pa_sample_spec*)); - break; - - case PA_TAG_ARBITRARY: { - void *p = va_arg(va, void*); - size_t size = va_arg(va, size_t); - pa_tagstruct_put_arbitrary(t, p, size); - break; - } - - case PA_TAG_BOOLEAN_TRUE: - case PA_TAG_BOOLEAN_FALSE: - pa_tagstruct_put_boolean(t, va_arg(va, int)); - break; - - case PA_TAG_TIMEVAL: - pa_tagstruct_put_timeval(t, va_arg(va, struct timeval*)); - break; - - case PA_TAG_USEC: - pa_tagstruct_put_usec(t, va_arg(va, pa_usec_t)); - break; - - case PA_TAG_CHANNEL_MAP: - pa_tagstruct_put_channel_map(t, va_arg(va, pa_channel_map *)); - break; - - case PA_TAG_CVOLUME: - pa_tagstruct_put_cvolume(t, va_arg(va, pa_cvolume *)); - break; - - default: - abort(); - } - } - - va_end(va); -} - -int pa_tagstruct_get(pa_tagstruct *t, ...) { - va_list va; - int ret = 0; - - assert(t); - - va_start(va, t); - while (ret == 0) { - int tag = va_arg(va, int); - - if (tag == PA_TAG_INVALID) - break; - - switch (tag) { - case PA_TAG_STRING: - case PA_TAG_STRING_NULL: - ret = pa_tagstruct_gets(t, va_arg(va, const char**)); - break; - - case PA_TAG_U32: - ret = pa_tagstruct_getu32(t, va_arg(va, uint32_t*)); - break; - - case PA_TAG_U8: - ret = pa_tagstruct_getu8(t, va_arg(va, uint8_t*)); - break; - - case PA_TAG_U64: - ret = pa_tagstruct_getu64(t, va_arg(va, uint64_t*)); - break; - - case PA_TAG_SAMPLE_SPEC: - ret = pa_tagstruct_get_sample_spec(t, va_arg(va, pa_sample_spec*)); - break; - - case PA_TAG_ARBITRARY: { - const void **p = va_arg(va, const void**); - size_t size = va_arg(va, size_t); - ret = pa_tagstruct_get_arbitrary(t, p, size); - break; - } - - case PA_TAG_BOOLEAN_TRUE: - case PA_TAG_BOOLEAN_FALSE: - ret = pa_tagstruct_get_boolean(t, va_arg(va, int*)); - break; - - case PA_TAG_TIMEVAL: - ret = pa_tagstruct_get_timeval(t, va_arg(va, struct timeval*)); - break; - - case PA_TAG_USEC: - ret = pa_tagstruct_get_usec(t, va_arg(va, pa_usec_t*)); - break; - - case PA_TAG_CHANNEL_MAP: - ret = pa_tagstruct_get_channel_map(t, va_arg(va, pa_channel_map *)); - break; - - case PA_TAG_CVOLUME: - ret = pa_tagstruct_get_cvolume(t, va_arg(va, pa_cvolume *)); - break; - - - default: - abort(); - } - - } - - va_end(va); - return ret; -} diff --git a/polyp/tagstruct.h b/polyp/tagstruct.h deleted file mode 100644 index b41936ff..00000000 --- a/polyp/tagstruct.h +++ /dev/null @@ -1,92 +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 -#include -#include -#include - -#include "sample.h" -#include "channelmap.h" -#include "volume.h" - -typedef struct pa_tagstruct pa_tagstruct; - -enum { - PA_TAG_INVALID = 0, - PA_TAG_STRING = 't', - PA_TAG_STRING_NULL = 'N', - PA_TAG_U32 = 'L', - PA_TAG_U8 = 'B', - PA_TAG_U64 = 'R', - PA_TAG_SAMPLE_SPEC = 'a', - PA_TAG_ARBITRARY = 'x', - PA_TAG_BOOLEAN_TRUE = '1', - PA_TAG_BOOLEAN_FALSE = '0', - PA_TAG_BOOLEAN = PA_TAG_BOOLEAN_TRUE, - PA_TAG_TIMEVAL = 'T', - PA_TAG_USEC = 'U' /* 64bit unsigned */, - PA_TAG_CHANNEL_MAP = 'm', - PA_TAG_CVOLUME = 'v' -}; - - - -pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length); -void pa_tagstruct_free(pa_tagstruct*t); -uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l); - -int pa_tagstruct_eof(pa_tagstruct*t); -const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l); - -void pa_tagstruct_put(pa_tagstruct *t, ...); - -void pa_tagstruct_puts(pa_tagstruct*t, const char *s); -void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c); -void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i); -void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t i); -void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss); -void pa_tagstruct_put_arbitrary(pa_tagstruct*t, const void *p, size_t length); -void pa_tagstruct_put_boolean(pa_tagstruct*t, int b); -void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv); -void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u); -void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map); -void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume); - -int pa_tagstruct_get(pa_tagstruct *t, ...); - -int pa_tagstruct_gets(pa_tagstruct*t, const char **s); -int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c); -int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i); -int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *i); -int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss); -int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length); -int pa_tagstruct_get_boolean(pa_tagstruct *t, int *b); -int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv); -int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u); -int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map); -int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *v); - - -#endif diff --git a/polyp/tokenizer.c b/polyp/tokenizer.c deleted file mode 100644 index 5e0c1b16..00000000 --- a/polyp/tokenizer.c +++ /dev/null @@ -1,88 +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 -#endif - -#include -#include -#include - -#include "tokenizer.h" -#include "dynarray.h" -#include "xmalloc.h" -#include "gccmacro.h" - -struct pa_tokenizer { - pa_dynarray *dynarray; -}; - -static void token_free(void *p, PA_GCC_UNUSED void *userdata) { - pa_xfree(p); -} - -static void parse(pa_dynarray*a, const char *s, unsigned args) { - int infty = 0; - const char delimiter[] = " \t\n\r"; - const char *p; - assert(a && s); - - if (args == 0) - infty = 1; - - p = s+strspn(s, delimiter); - while (*p && (infty || args >= 2)) { - size_t l = strcspn(p, delimiter); - char *n = pa_xstrndup(p, l); - pa_dynarray_append(a, n); - p += l; - p += strspn(p, delimiter); - args--; - } - - if (args && *p) { - char *n = pa_xstrdup(p); - pa_dynarray_append(a, n); - } -} - -pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args) { - pa_tokenizer *t; - - t = pa_xmalloc(sizeof(pa_tokenizer)); - t->dynarray = pa_dynarray_new(); - assert(t->dynarray); - - parse(t->dynarray, s, args); - return t; -} - -void pa_tokenizer_free(pa_tokenizer *t) { - assert(t); - pa_dynarray_free(t->dynarray, token_free, NULL); - pa_xfree(t); -} - -const char *pa_tokenizer_get(pa_tokenizer *t, unsigned i) { - assert(t); - return pa_dynarray_get(t->dynarray, i); -} diff --git a/polyp/tokenizer.h b/polyp/tokenizer.h deleted file mode 100644 index bedacb8a..00000000 --- a/polyp/tokenizer.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef footokenizerhfoo -#define footokenizerhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -typedef struct pa_tokenizer pa_tokenizer; - -pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args); -void pa_tokenizer_free(pa_tokenizer *t); - -const char *pa_tokenizer_get(pa_tokenizer *t, unsigned i); - -#endif diff --git a/polyp/util.c b/polyp/util.c deleted file mode 100644 index 4b6edb97..00000000 --- a/polyp/util.c +++ /dev/null @@ -1,1176 +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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SCHED_H -#include -#endif - -#ifdef HAVE_SYS_RESOURCE_H -#include -#endif - -#ifdef HAVE_PTHREAD -#include -#endif - -#ifdef HAVE_NETDB_H -#include -#endif - -#ifdef HAVE_WINDOWS_H -#include -#endif - -#include - -#ifdef HAVE_PWD_H -#include -#endif -#ifdef HAVE_GRP_H -#include -#endif - -#include "winsock.h" - -#include "util.h" -#include "xmalloc.h" -#include "log.h" - -#ifndef OS_IS_WIN32 -#define PA_RUNTIME_PATH_PREFIX "/tmp/polypaudio-" -#define PATH_SEP '/' -#else -#define PA_RUNTIME_PATH_PREFIX "%TEMP%\\polypaudio-" -#define PATH_SEP '\\' -#endif - -#ifdef OS_IS_WIN32 - -#define POLYP_ROOTENV "POLYP_ROOT" - -int pa_set_root(HANDLE handle) { - char library_path[MAX_PATH + sizeof(POLYP_ROOTENV) + 1], *sep; - - strcpy(library_path, POLYP_ROOTENV "="); - - if (!GetModuleFileName(handle, library_path + sizeof(POLYP_ROOTENV), MAX_PATH)) - return 0; - - sep = strrchr(library_path, '\\'); - if (sep) - *sep = '\0'; - - if (_putenv(library_path) < 0) - return 0; - - return 1; -} - -#endif - -/** Make a file descriptor nonblock. Doesn't do any error checking */ -void pa_make_nonblock_fd(int fd) { -#ifdef O_NONBLOCK - int v; - assert(fd >= 0); - - if ((v = fcntl(fd, F_GETFL)) >= 0) - if (!(v & O_NONBLOCK)) - fcntl(fd, F_SETFL, v|O_NONBLOCK); -#elif defined(OS_IS_WIN32) - u_long arg = 1; - if (ioctlsocket(fd, FIONBIO, &arg) < 0) { - if (WSAGetLastError() == WSAENOTSOCK) - pa_log_warn(__FILE__": WARNING: Only sockets can be made non-blocking!\n"); - } -#else - pa_log_warn(__FILE__": WARNING: Non-blocking I/O not supported.!\n"); -#endif -} - -/** Creates a directory securely */ -int pa_make_secure_dir(const char* dir) { - struct stat st; - assert(dir); - -#ifdef OS_IS_WIN32 - if (mkdir(dir) < 0) -#else - if (mkdir(dir, 0700) < 0) -#endif - if (errno != EEXIST) - return -1; - -#ifdef HAVE_LSTAT - if (lstat(dir, &st) < 0) -#else - if (stat(dir, &st) < 0) -#endif - goto fail; - -#ifndef OS_IS_WIN32 - if (!S_ISDIR(st.st_mode) || (st.st_uid != getuid()) || ((st.st_mode & 0777) != 0700)) - goto fail; -#else - fprintf(stderr, "FIXME: pa_make_secure_dir()\n"); -#endif - - return 0; - -fail: - rmdir(dir); - return -1; -} - -/* Creates a the parent directory of the specified path securely */ -int pa_make_secure_parent_dir(const char *fn) { - int ret = -1; - char *slash, *dir = pa_xstrdup(fn); - - slash = pa_path_get_filename(dir); - if (slash == fn) - goto finish; - *(slash-1) = 0; - - if (pa_make_secure_dir(dir) < 0) - goto finish; - - ret = 0; - -finish: - pa_xfree(dir); - return ret; -} - - -/** Calls read() in a loop. Makes sure that as much as 'size' bytes, - * unless EOF is reached or an error occured */ -ssize_t pa_loop_read(int fd, void*data, size_t size) { - ssize_t ret = 0; - assert(fd >= 0 && data && size); - - while (size > 0) { - ssize_t r; - - if ((r = read(fd, data, size)) < 0) - return r; - - if (r == 0) - break; - - ret += r; - data = (uint8_t*) data + r; - size -= r; - } - - return ret; -} - -/** Similar to pa_loop_read(), but wraps write() */ -ssize_t pa_loop_write(int fd, const void*data, size_t size) { - ssize_t ret = 0; - assert(fd >= 0 && data && size); - - while (size > 0) { - ssize_t r; - - if ((r = write(fd, data, size)) < 0) - return r; - - if (r == 0) - break; - - ret += r; - data = (const uint8_t*) data + r; - size -= r; - } - - return ret; -} - -/* Print a warning messages in case that the given signal is not - * blocked or trapped */ -void pa_check_signal_is_blocked(int sig) { -#ifdef HAVE_SIGACTION - struct sigaction sa; - sigset_t set; - - /* If POSIX threads are supported use thread-aware - * pthread_sigmask() function, to check if the signal is - * blocked. Otherwise fall back to sigprocmask() */ - -#ifdef HAVE_PTHREAD - if (pthread_sigmask(SIG_SETMASK, NULL, &set) < 0) { -#endif - if (sigprocmask(SIG_SETMASK, NULL, &set) < 0) { - pa_log(__FILE__": sigprocmask() failed: %s\n", strerror(errno)); - return; - } -#ifdef HAVE_PTHREAD - } -#endif - - if (sigismember(&set, sig)) - return; - - /* Check whether the signal is trapped */ - - if (sigaction(sig, NULL, &sa) < 0) { - pa_log(__FILE__": sigaction() failed: %s\n", strerror(errno)); - return; - } - - if (sa.sa_handler != SIG_DFL) - return; - - pa_log(__FILE__": WARNING: %s is not trapped. This might cause malfunction!\n", pa_strsignal(sig)); -#else /* HAVE_SIGACTION */ - pa_log(__FILE__": WARNING: %s might not be trapped. This might cause malfunction!\n", pa_strsignal(sig)); -#endif -} - -/* The following function is based on an example from the GNU libc - * documentation. This function is similar to GNU's asprintf(). */ -char *pa_sprintf_malloc(const char *format, ...) { - int size = 100; - char *c = NULL; - - assert(format); - - for(;;) { - int r; - va_list ap; - - c = pa_xrealloc(c, size); - - va_start(ap, format); - r = vsnprintf(c, size, format, ap); - va_end(ap); - - if (r > -1 && r < size) - return c; - - if (r > -1) /* glibc 2.1 */ - size = r+1; - else /* glibc 2.0 */ - size *= 2; - } -} - -/* Same as the previous function, but use a va_list instead of an - * ellipsis */ -char *pa_vsprintf_malloc(const char *format, va_list ap) { - int size = 100; - char *c = NULL; - - assert(format); - - for(;;) { - int r; - c = pa_xrealloc(c, size); - r = vsnprintf(c, size, format, ap); - - if (r > -1 && r < size) - return c; - - if (r > -1) /* glibc 2.1 */ - size = r+1; - else /* glibc 2.0 */ - size *= 2; - } -} - -/* Return the current username in the specified string buffer. */ -char *pa_get_user_name(char *s, size_t l) { - char *p; - char buf[1024]; - -#ifdef HAVE_PWD_H - struct passwd pw, *r; -#endif - - assert(s && l > 0); - - if (!(p = getenv("USER")) && !(p = getenv("LOGNAME")) && !(p = getenv("USERNAME"))) { -#ifdef HAVE_PWD_H - -#ifdef HAVE_GETPWUID_R - if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { -#else - /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) - * that do not support getpwuid_r. */ - if ((r = getpwuid(getuid())) == NULL) { -#endif - snprintf(s, l, "%lu", (unsigned long) getuid()); - return s; - } - - p = r->pw_name; - -#elif defined(OS_IS_WIN32) /* HAVE_PWD_H */ - DWORD size = sizeof(buf); - - if (!GetUserName(buf, &size)) - return NULL; - - p = buf; - -#else /* HAVE_PWD_H */ - return NULL; -#endif /* HAVE_PWD_H */ - } - - return pa_strlcpy(s, p, l); -} - -/* Return the current hostname in the specified buffer. */ -char *pa_get_host_name(char *s, size_t l) { - assert(s && l > 0); - if (gethostname(s, l) < 0) { - pa_log(__FILE__": gethostname(): %s\n", strerror(errno)); - return NULL; - } - s[l-1] = 0; - return s; -} - -/* Return the home directory of the current user */ -char *pa_get_home_dir(char *s, size_t l) { - char *e; - -#ifdef HAVE_PWD_H - char buf[1024]; - struct passwd pw, *r; -#endif - - assert(s && l); - - if ((e = getenv("HOME"))) - return pa_strlcpy(s, e, l); - - if ((e = getenv("USERPROFILE"))) - return pa_strlcpy(s, e, l); - -#ifdef HAVE_PWD_H -#ifdef HAVE_GETPWUID_R - if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { - pa_log(__FILE__": getpwuid_r() failed\n"); -#else - /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) - * that do not support getpwuid_r. */ - if ((r = getpwuid(getuid())) == NULL) { - pa_log(__FILE__": getpwuid_r() failed\n"); -#endif - return NULL; - } - - return pa_strlcpy(s, r->pw_dir, l); -#else /* HAVE_PWD_H */ - return NULL; -#endif -} - -/* Similar to OpenBSD's strlcpy() function */ -char *pa_strlcpy(char *b, const char *s, size_t l) { - assert(b && s && l > 0); - - strncpy(b, s, l); - b[l-1] = 0; - return b; -} - -int pa_gettimeofday(struct timeval *tv) { -#ifdef HAVE_GETTIMEOFDAY - return gettimeofday(tv, NULL); -#elif defined(OS_IS_WIN32) - /* - * Copied from implementation by Steven Edwards (LGPL). - * Found on wine mailing list. - */ - -#if defined(_MSC_VER) || defined(__BORLANDC__) -#define EPOCHFILETIME (116444736000000000i64) -#else -#define EPOCHFILETIME (116444736000000000LL) -#endif - - FILETIME ft; - LARGE_INTEGER li; - __int64 t; - - if (tv) { - GetSystemTimeAsFileTime(&ft); - li.LowPart = ft.dwLowDateTime; - li.HighPart = ft.dwHighDateTime; - t = li.QuadPart; /* In 100-nanosecond intervals */ - t -= EPOCHFILETIME; /* Offset to the Epoch time */ - t /= 10; /* In microseconds */ - tv->tv_sec = (long)(t / 1000000); - tv->tv_usec = (long)(t % 1000000); - } - - return 0; -#else -#error "Platform lacks gettimeofday() or equivalent function." -#endif -} - -/* Calculate the difference between the two specfified timeval - * timestamsps. */ -pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) { - pa_usec_t r; - assert(a && b); - - /* Check which whan is the earlier time and swap the two arguments if reuqired. */ - if (pa_timeval_cmp(a, b) < 0) { - const struct timeval *c; - c = a; - a = b; - b = c; - } - - /* Calculate the second difference*/ - r = ((pa_usec_t) a->tv_sec - b->tv_sec)* 1000000; - - /* Calculate the microsecond difference */ - if (a->tv_usec > b->tv_usec) - r += ((pa_usec_t) a->tv_usec - b->tv_usec); - else if (a->tv_usec < b->tv_usec) - r -= ((pa_usec_t) b->tv_usec - a->tv_usec); - - return r; -} - -/* Compare the two timeval structs and return 0 when equal, negative when a < b, positive otherwse */ -int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) { - assert(a && b); - - if (a->tv_sec < b->tv_sec) - return -1; - - if (a->tv_sec > b->tv_sec) - return 1; - - if (a->tv_usec < b->tv_usec) - return -1; - - if (a->tv_usec > b->tv_usec) - return 1; - - return 0; -} - -/* Return the time difference between now and the specified timestamp */ -pa_usec_t pa_timeval_age(const struct timeval *tv) { - struct timeval now; - assert(tv); - pa_gettimeofday(&now); - return pa_timeval_diff(&now, tv); -} - -/* Add the specified time inmicroseconds to the specified timeval structure */ -void pa_timeval_add(struct timeval *tv, pa_usec_t v) { - unsigned long secs; - assert(tv); - - secs = (v/1000000); - tv->tv_sec += (unsigned long) secs; - v -= secs*1000000; - - tv->tv_usec += v; - - /* Normalize */ - while (tv->tv_usec >= 1000000) { - tv->tv_sec++; - tv->tv_usec -= 1000000; - } -} - -#define NICE_LEVEL (-15) - -/* Raise the priority of the current process as much as possible and -sensible: set the nice level to -15 and enable realtime scheduling if -supported.*/ -void pa_raise_priority(void) { - -#ifdef HAVE_SYS_RESOURCE_H - if (setpriority(PRIO_PROCESS, 0, NICE_LEVEL) < 0) - pa_log_warn(__FILE__": setpriority() failed: %s\n", strerror(errno)); - else - pa_log_info(__FILE__": Successfully gained nice level %i.\n", NICE_LEVEL); -#endif - -#ifdef _POSIX_PRIORITY_SCHEDULING - { - struct sched_param sp; - - if (sched_getparam(0, &sp) < 0) { - pa_log(__FILE__": sched_getparam() failed: %s\n", strerror(errno)); - return; - } - - sp.sched_priority = 1; - if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) { - pa_log_warn(__FILE__": sched_setscheduler() failed: %s\n", strerror(errno)); - return; - } - - pa_log_info(__FILE__": Successfully enabled SCHED_FIFO scheduling.\n"); - } -#endif - -#ifdef OS_IS_WIN32 - if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) - pa_log_warn(__FILE__": SetPriorityClass() failed: 0x%08X\n", GetLastError()); - else - pa_log_info(__FILE__": Successfully gained high priority class.\n"); -#endif -} - -/* Reset the priority to normal, inverting the changes made by pa_raise_priority() */ -void pa_reset_priority(void) { -#ifdef OS_IS_WIN32 - SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); -#endif - -#ifdef _POSIX_PRIORITY_SCHEDULING - { - struct sched_param sp; - sched_getparam(0, &sp); - sp.sched_priority = 0; - sched_setscheduler(0, SCHED_OTHER, &sp); - } -#endif - -#ifdef HAVE_SYS_RESOURCE_H - setpriority(PRIO_PROCESS, 0, 0); -#endif -} - -/* Set the FD_CLOEXEC flag for a fd */ -int pa_fd_set_cloexec(int fd, int b) { - -#ifdef FD_CLOEXEC - int v; - assert(fd >= 0); - - if ((v = fcntl(fd, F_GETFD, 0)) < 0) - return -1; - - v = (v & ~FD_CLOEXEC) | (b ? FD_CLOEXEC : 0); - - if (fcntl(fd, F_SETFD, v) < 0) - return -1; -#endif - - return 0; -} - -/* Return the binary file name of the current process. Works on Linux - * only. This shoul be used for eyecandy only, don't rely on return - * non-NULL! */ -char *pa_get_binary_name(char *s, size_t l) { - -#ifdef HAVE_READLINK - char path[PATH_MAX]; - int i; - assert(s && l); - - /* This works on Linux only */ - - snprintf(path, sizeof(path), "/proc/%u/exe", (unsigned) getpid()); - if ((i = readlink(path, s, l-1)) < 0) - return NULL; - - s[i] = 0; - return s; -#elif defined(OS_IS_WIN32) - char path[PATH_MAX]; - if (!GetModuleFileName(NULL, path, PATH_MAX)) - return NULL; - pa_strlcpy(s, pa_path_get_filename(path), l); - return s; -#else - return NULL; -#endif -} - -/* Return a pointer to the filename inside a path (which is the last - * component). */ -char *pa_path_get_filename(const char *p) { - char *fn; - - if ((fn = strrchr(p, PATH_SEP))) - return fn+1; - - return (char*) p; -} - -/* Try to parse a boolean string value.*/ -int pa_parse_boolean(const char *v) { - - if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on")) - return 1; - else if (!strcmp(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off")) - return 0; - - return -1; -} - -/* Split the specified string wherever one of the strings in delimiter - * occurs. Each time it is called returns a newly allocated string - * with pa_xmalloc(). The variable state points to, should be - * initiallized to NULL before the first call. */ -char *pa_split(const char *c, const char *delimiter, const char**state) { - const char *current = *state ? *state : c; - size_t l; - - if (!*current) - return NULL; - - l = strcspn(current, delimiter); - *state = current+l; - - if (**state) - (*state)++; - - return pa_xstrndup(current, l); -} - -/* What is interpreted as whitespace? */ -#define WHITESPACE " \t\n" - -/* Split a string into words. Otherwise similar to pa_split(). */ -char *pa_split_spaces(const char *c, const char **state) { - const char *current = *state ? *state : c; - size_t l; - - if (!*current || *c == 0) - return NULL; - - current += strspn(current, WHITESPACE); - l = strcspn(current, WHITESPACE); - - *state = current+l; - - return pa_xstrndup(current, l); -} - -/* Return the name of an UNIX signal. Similar to GNU's strsignal() */ -const char *pa_strsignal(int sig) { - switch(sig) { - case SIGINT: return "SIGINT"; - case SIGTERM: return "SIGTERM"; -#ifdef SIGUSR1 - case SIGUSR1: return "SIGUSR1"; -#endif -#ifdef SIGUSR2 - case SIGUSR2: return "SIGUSR2"; -#endif -#ifdef SIGXCPU - case SIGXCPU: return "SIGXCPU"; -#endif -#ifdef SIGPIPE - case SIGPIPE: return "SIGPIPE"; -#endif -#ifdef SIGCHLD - case SIGCHLD: return "SIGCHLD"; -#endif -#ifdef SIGHUP - case SIGHUP: return "SIGHUP"; -#endif - default: return "UNKNOWN SIGNAL"; - } -} - -#ifdef HAVE_GRP_H - -/* Check whether the specified GID and the group name match */ -static int is_group(gid_t gid, const char *name) { - struct group group, *result = NULL; - long n; - void *data; - int r = -1; - -#ifdef HAVE_GETGRGID_R -#ifdef _SC_GETGR_R_SIZE_MAX - n = sysconf(_SC_GETGR_R_SIZE_MAX); -#else - n = -1; -#endif - if (n < 0) n = 512; - data = pa_xmalloc(n); - - if (getgrgid_r(gid, &group, data, n, &result) < 0 || !result) { - pa_log(__FILE__ ": getgrgid_r(%u) failed: %s\n", gid, strerror(errno)); - goto finish; - } - - - r = strcmp(name, result->gr_name) == 0; - -finish: - pa_xfree(data); -#else - /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not - * support getgrgid_r. */ - if ((result = getgrgid(gid)) == NULL) { - pa_log(__FILE__ ": getgrgid(%u) failed: %s\n", gid, strerror(errno)); - goto finish; - } - - r = strcmp(name, result->gr_name) == 0; - -finish: -#endif - - return r; -} - -/* Check the current user is member of the specified group */ -int pa_uid_in_group(const char *name, gid_t *gid) { - GETGROUPS_T *gids, tgid; - int n = sysconf(_SC_NGROUPS_MAX); - int r = -1, i; - - assert(n > 0); - - gids = pa_xmalloc(sizeof(GETGROUPS_T)*n); - - if ((n = getgroups(n, gids)) < 0) { - pa_log(__FILE__": getgroups() failed: %s\n", strerror(errno)); - goto finish; - } - - for (i = 0; i < n; i++) { - if (is_group(gids[i], name) > 0) { - *gid = gids[i]; - r = 1; - goto finish; - } - } - - if (is_group(tgid = getgid(), name) > 0) { - *gid = tgid; - r = 1; - goto finish; - } - - r = 0; - -finish: - - pa_xfree(gids); - return r; -} - -#else /* HAVE_GRP_H */ - -int pa_uid_in_group(const char *name, gid_t *gid) { - return -1; -} - -#endif - -/* Lock or unlock a file entirely. - (advisory on UNIX, mandatory on Windows) */ -int pa_lock_fd(int fd, int b) { -#ifdef F_SETLKW - struct flock flock; - - /* Try a R/W lock first */ - - flock.l_type = b ? F_WRLCK : F_UNLCK; - flock.l_whence = SEEK_SET; - flock.l_start = 0; - flock.l_len = 0; - - if (fcntl(fd, F_SETLKW, &flock) >= 0) - return 0; - - /* Perhaps the file descriptor qas opened for read only, than try again with a read lock. */ - if (b && errno == EBADF) { - flock.l_type = F_RDLCK; - if (fcntl(fd, F_SETLKW, &flock) >= 0) - return 0; - } - - pa_log(__FILE__": %slock failed: %s\n", !b ? "un" : "", strerror(errno)); -#endif - -#ifdef OS_IS_WIN32 - HANDLE h = (HANDLE)_get_osfhandle(fd); - - if (b && LockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF)) - return 0; - if (!b && UnlockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF)) - return 0; - - pa_log(__FILE__": %slock failed: 0x%08X\n", !b ? "un" : "", GetLastError()); -#endif - - return -1; -} - -/* Remove trailing newlines from a string */ -char* pa_strip_nl(char *s) { - assert(s); - - s[strcspn(s, "\r\n")] = 0; - return s; -} - -/* Create a temporary lock file and lock it. */ -int pa_lock_lockfile(const char *fn) { - int fd = -1; - assert(fn); - - for (;;) { - struct stat st; - - if ((fd = open(fn, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)) < 0) { - pa_log(__FILE__": failed to create lock file '%s': %s\n", fn, strerror(errno)); - goto fail; - } - - if (pa_lock_fd(fd, 1) < 0) { - pa_log(__FILE__": failed to lock file '%s'.\n", fn); - goto fail; - } - - if (fstat(fd, &st) < 0) { - pa_log(__FILE__": failed to fstat() file '%s'.\n", fn); - goto fail; - } - - /* Check wheter the file has been removed meanwhile. When yes, restart this loop, otherwise, we're done */ - if (st.st_nlink >= 1) - break; - - if (pa_lock_fd(fd, 0) < 0) { - pa_log(__FILE__": failed to unlock file '%s'.\n", fn); - goto fail; - } - - if (close(fd) < 0) { - pa_log(__FILE__": failed to close file '%s'.\n", fn); - goto fail; - } - - fd = -1; - } - - return fd; - -fail: - - if (fd >= 0) - close(fd); - - return -1; -} - -/* Unlock a temporary lcok file */ -int pa_unlock_lockfile(const char *fn, int fd) { - int r = 0; - assert(fn && fd >= 0); - - if (unlink(fn) < 0) { - pa_log_warn(__FILE__": WARNING: unable to remove lock file '%s': %s\n", fn, strerror(errno)); - r = -1; - } - - if (pa_lock_fd(fd, 0) < 0) { - pa_log_warn(__FILE__": WARNING: failed to unlock file '%s'.\n", fn); - r = -1; - } - - if (close(fd) < 0) { - pa_log_warn(__FILE__": WARNING: failed to close lock file '%s': %s\n", fn, strerror(errno)); - r = -1; - } - - return r; -} - -/* Try to open a configuration file. If "env" is specified, open the - * value of the specified environment variable. Otherwise look for a - * file "local" in the home directory or a file "global" in global - * file system. If "result" is non-NULL, a pointer to a newly - * allocated buffer containing the used configuration file is - * stored there.*/ -FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) { - const char *fn; - char h[PATH_MAX]; - -#ifdef OS_IS_WIN32 - char buf[PATH_MAX]; - - if (!getenv(POLYP_ROOTENV)) - pa_set_root(NULL); -#endif - - if (env && (fn = getenv(env))) { -#ifdef OS_IS_WIN32 - if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX)) - return NULL; - fn = buf; -#endif - - if (result) - *result = pa_xstrdup(fn); - - return fopen(fn, "r"); - } - - if (local && pa_get_home_dir(h, sizeof(h))) { - FILE *f; - char *lfn; - - fn = lfn = pa_sprintf_malloc("%s/%s", h, local); - -#ifdef OS_IS_WIN32 - if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) - return NULL; - fn = buf; -#endif - - f = fopen(fn, "r"); - - if (f || errno != ENOENT) { - if (result) - *result = pa_xstrdup(fn); - pa_xfree(lfn); - return f; - } - - pa_xfree(lfn); - } - - if (!global) { - if (result) - *result = NULL; - errno = ENOENT; - return NULL; - } - -#ifdef OS_IS_WIN32 - if (!ExpandEnvironmentStrings(global, buf, PATH_MAX)) - return NULL; - global = buf; -#endif - - if (result) - *result = pa_xstrdup(global); - - return fopen(global, "r"); -} - -/* Format the specified data as a hexademical string */ -char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength) { - size_t i = 0, j = 0; - const char hex[] = "0123456789abcdef"; - assert(d && s && slength > 0); - - while (i < dlength && j+3 <= slength) { - s[j++] = hex[*d >> 4]; - s[j++] = hex[*d & 0xF]; - - d++; - i++; - } - - s[j < slength ? j : slength] = 0; - return s; -} - -/* Convert a hexadecimal digit to a number or -1 if invalid */ -static int hexc(char c) { - if (c >= '0' && c <= '9') - return c - '0'; - - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - - return -1; -} - -/* Parse a hexadecimal string as created by pa_hexstr() to a BLOB */ -size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) { - size_t j = 0; - assert(p && d); - - while (j < dlength && *p) { - int b; - - if ((b = hexc(*(p++))) < 0) - return (size_t) -1; - - d[j] = (uint8_t) (b << 4); - - if (!*p) - return (size_t) -1; - - if ((b = hexc(*(p++))) < 0) - return (size_t) -1; - - d[j] |= (uint8_t) b; - j++; - } - - return j; -} - -/* Return the fully qualified domain name in *s */ -char *pa_get_fqdn(char *s, size_t l) { - char hn[256]; -#ifdef HAVE_GETADDRINFO - struct addrinfo *a, hints; -#endif - - if (!pa_get_host_name(hn, sizeof(hn))) - return NULL; - -#ifdef HAVE_GETADDRINFO - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_flags = AI_CANONNAME; - - if (getaddrinfo(hn, NULL, &hints, &a) < 0 || !a || !a->ai_canonname || !*a->ai_canonname) - return pa_strlcpy(s, hn, l); - - pa_strlcpy(s, a->ai_canonname, l); - freeaddrinfo(a); - return s; -#else - return pa_strlcpy(s, hn, l); -#endif -} - -/* Returns nonzero when *s starts with *pfx */ -int pa_startswith(const char *s, const char *pfx) { - size_t l; - assert(s && pfx); - l = strlen(pfx); - - return strlen(s) >= l && strncmp(s, pfx, l) == 0; -} - -/* if fn is null return the polypaudio run time path in s (/tmp/polypaudio) - * if fn is non-null and starts with / return fn in s - * otherwise append fn to the run time path and return it in s */ -char *pa_runtime_path(const char *fn, char *s, size_t l) { - char u[256]; - -#ifndef OS_IS_WIN32 - if (fn && *fn == '/') -#else - if (fn && strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\') -#endif - return pa_strlcpy(s, fn, l); - - if (fn) - snprintf(s, l, "%s%s%c%s", PA_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PATH_SEP, fn); - else - snprintf(s, l, "%s%s", PA_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u))); - -#ifdef OS_IS_WIN32 - { - char buf[l]; - strcpy(buf, s); - ExpandEnvironmentStrings(buf, s, l); - } -#endif - - return s; -} - -/* Wait t milliseconds */ -int pa_msleep(unsigned long t) { -#ifdef OS_IS_WIN32 - Sleep(t); - return 0; -#elif defined(HAVE_NANOSLEEP) - struct timespec ts; - - ts.tv_sec = t/1000; - ts.tv_nsec = (t % 1000) * 1000000; - - return nanosleep(&ts, NULL); -#else -#error "Platform lacks a sleep function." -#endif -} - -/* Convert the string s to a signed integer in *ret_i */ -int pa_atoi(const char *s, int32_t *ret_i) { - char *x = NULL; - long l; - assert(s && ret_i); - - l = strtol(s, &x, 0); - - if (!x || *x) - return -1; - - *ret_i = (int32_t) l; - - return 0; -} - -/* Convert the string s to an unsigned integer in *ret_u */ -int pa_atou(const char *s, uint32_t *ret_u) { - char *x = NULL; - unsigned long l; - assert(s && ret_u); - - l = strtoul(s, &x, 0); - - if (!x || *x) - return -1; - - *ret_u = (uint32_t) l; - - return 0; -} diff --git a/polyp/util.h b/polyp/util.h deleted file mode 100644 index 95e7b99b..00000000 --- a/polyp/util.h +++ /dev/null @@ -1,99 +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 -#include -#include -#include - -#include "gccmacro.h" -#include "sample.h" - -struct timeval; - -void pa_make_nonblock_fd(int fd); - -int pa_make_secure_dir(const char* dir); -int pa_make_secure_parent_dir(const char *fn); - -ssize_t pa_loop_read(int fd, void*data, size_t size); -ssize_t pa_loop_write(int fd, const void*data, size_t size); - -void pa_check_signal_is_blocked(int sig); - -char *pa_sprintf_malloc(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); -char *pa_vsprintf_malloc(const char *format, va_list ap); - -char *pa_strlcpy(char *b, const char *s, size_t l); - -char *pa_get_user_name(char *s, size_t l); -char *pa_get_host_name(char *s, size_t l); -char *pa_get_fqdn(char *s, size_t l); -char *pa_get_binary_name(char *s, size_t l); -char *pa_get_home_dir(char *s, size_t l); - -char *pa_path_get_filename(const char *p); - -int pa_gettimeofday(struct timeval *tv); -pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b); -int pa_timeval_cmp(const struct timeval *a, const struct timeval *b); -pa_usec_t pa_timeval_age(const struct timeval *tv); -void pa_timeval_add(struct timeval *tv, pa_usec_t v); - -void pa_raise_priority(void); -void pa_reset_priority(void); - -int pa_fd_set_cloexec(int fd, int b); - -int pa_parse_boolean(const char *s); - -char *pa_split(const char *c, const char*delimiters, const char **state); -char *pa_split_spaces(const char *c, const char **state); - -char *pa_strip_nl(char *s); - -const char *pa_strsignal(int sig); - -int pa_uid_in_group(const char *name, gid_t *gid); - -int pa_lock_fd(int fd, int b); - -int pa_lock_lockfile(const char *fn); -int pa_unlock_lockfile(const char *fn, int fd); - -FILE *pa_open_config_file(const char *env, const char *global, const char *local, char **result); - -char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength); -size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength); - -int pa_startswith(const char *s, const char *pfx); - -char *pa_runtime_path(const char *fn, char *s, size_t l); - -int pa_msleep(unsigned long t); - -int pa_atoi(const char *s, int32_t *ret_i); -int pa_atou(const char *s, uint32_t *ret_u); - -#endif diff --git a/polyp/voltest.c b/polyp/voltest.c deleted file mode 100644 index 917e04d3..00000000 --- a/polyp/voltest.c +++ /dev/null @@ -1,20 +0,0 @@ -/* $Id$ */ - -#include - -#include -#include "gccmacro.h" - -int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { - pa_volume_t v; - - for (v = PA_VOLUME_MUTED; v <= PA_VOLUME_NORM*2; v += 256) { - - double dB = pa_sw_volume_to_dB(v); - double f = pa_sw_volume_to_linear(v); - - printf("Volume: %3i; percent: %i%%; decibel %0.2f; linear = %0.2f; volume(decibel): %3i; volume(linear): %3i\n", - v, (v*100)/PA_VOLUME_NORM, dB, f, pa_sw_volume_from_dB(dB), pa_sw_volume_from_linear(f)); - - } -} diff --git a/polyp/volume.c b/polyp/volume.c deleted file mode 100644 index 0f153141..00000000 --- a/polyp/volume.c +++ /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 -#endif - -#include -#include -#include - -#include "volume.h" - -int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) { - int i; - assert(a); - assert(b); - - if (a->channels != b->channels) - return 0; - - for (i = 0; i < a->channels; i++) - if (a->values[i] != b->values[i]) - return 0; - - return 1; -} - -pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) { - int i; - - assert(a); - assert(channels > 0); - assert(channels <= PA_CHANNELS_MAX); - - a->channels = channels; - - for (i = 0; i < a->channels; i++) - a->values[i] = v; - - return a; -} - -pa_volume_t pa_cvolume_avg(const pa_cvolume *a) { - uint64_t sum = 0; - int i; - assert(a); - - for (i = 0; i < a->channels; i++) - sum += a->values[i]; - - sum /= a->channels; - - return (pa_volume_t) sum; -} - -pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) { - return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a)* pa_sw_volume_to_linear(b)); -} - -#define USER_DECIBEL_RANGE 30 - -pa_volume_t pa_sw_volume_from_dB(double dB) { - if (dB <= -USER_DECIBEL_RANGE) - return PA_VOLUME_MUTED; - - return (pa_volume_t) ((dB/USER_DECIBEL_RANGE+1)*PA_VOLUME_NORM); -} - -double pa_sw_volume_to_dB(pa_volume_t v) { - if (v == PA_VOLUME_MUTED) - return PA_DECIBEL_MININFTY; - - return ((double) v/PA_VOLUME_NORM-1)*USER_DECIBEL_RANGE; -} - -pa_volume_t pa_sw_volume_from_linear(double v) { - - if (v <= 0) - return PA_VOLUME_MUTED; - - if (v > .999 && v < 1.001) - return PA_VOLUME_NORM; - - return pa_sw_volume_from_dB(20*log10(v)); -} - -double pa_sw_volume_to_linear(pa_volume_t v) { - - if (v == PA_VOLUME_MUTED) - return 0; - - return pow(10, pa_sw_volume_to_dB(v)/20); -} - -char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) { - unsigned channel; - int first = 1; - char *e; - - assert(s); - assert(l > 0); - assert(c); - - *(e = s) = 0; - - for (channel = 0; channel < c->channels && l > 1; channel++) { - l -= snprintf(e, l, "%s%u: %3u%%", - first ? "" : " ", - channel, - (c->values[channel]*100)/PA_VOLUME_NORM); - - e = strchr(e, 0); - first = 0; - } - - return s; -} - -/** Return non-zero if the volume of all channels is equal to the specified value */ -int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) { - unsigned c; - assert(a); - - for (c = 0; c < a->channels; c++) - if (a->values[c] != v) - return 0; - - return 1; -} - -pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { - unsigned i; - - assert(dest); - assert(a); - assert(b); - - for (i = 0; i < a->channels || i < b->channels || i < PA_CHANNELS_MAX; i++) { - - dest->values[i] = pa_sw_volume_multiply( - i < a->channels ? a->values[i] : PA_VOLUME_NORM, - i < b->channels ? b->values[i] : PA_VOLUME_NORM); - } - - dest->channels = i; - - return dest; -} - -int pa_cvolume_valid(const pa_cvolume *v) { - assert(v); - - if (v->channels <= 0 || v->channels > PA_CHANNELS_MAX) - return 0; - - return 1; -} diff --git a/polyp/volume.h b/polyp/volume.h deleted file mode 100644 index b2a48084..00000000 --- a/polyp/volume.h +++ /dev/null @@ -1,107 +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 -#include -#include - -/** \file - * Constants and routines for volume handling */ - -PA_C_DECL_BEGIN - -/** Volume specification: - * PA_VOLUME_MUTED: silence; - * < PA_VOLUME_NORM: decreased volume; - * PA_VOLUME_NORM: normal volume; - * > PA_VOLUME_NORM: increased volume */ -typedef uint32_t pa_volume_t; - -/** Normal volume (100%) */ -#define PA_VOLUME_NORM (0x10000) - -/** Muted volume (0%) */ -#define PA_VOLUME_MUTED (0) - -/** A structure encapsulating a per-channel volume */ -typedef struct pa_cvolume { - uint8_t channels; - pa_volume_t values[PA_CHANNELS_MAX]; -} pa_cvolume; - -/** Return non-zero when *a == *b */ -int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b); - -/** Set the volume of all channels to PA_VOLUME_NORM */ -#define pa_cvolume_reset(a, n) pa_cvolume_set((a), (n), PA_VOLUME_NORM) - -/** Set the volume of all channels to PA_VOLUME_MUTED */ -#define pa_cvolume_mute(a, n) pa_cvolume_set((a), (n), PA_VOLUME_MUTED) - -/** Set the volume of all channels to the specified parameter */ -pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v); - -/** Pretty print a volume structure */ -#define PA_CVOLUME_SNPRINT_MAX 64 -char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c); - -/** Return the average volume of all channels */ -pa_volume_t pa_cvolume_avg(const pa_cvolume *a); - -/** Return TRUE when the passed cvolume structure is valid, FALSE otherwise */ -int pa_cvolume_valid(const pa_cvolume *v); - -/** Return non-zero if the volume of all channels is equal to the specified value */ -int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v); - -#define pa_cvolume_is_muted(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_MUTED) -#define pa_cvolume_is_norm(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_NORM) - -/** Multiply two volumes specifications, return the result. This uses PA_VOLUME_NORM as neutral element of multiplication. */ -pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b); - -pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b); - -/** Convert a decibel value to a volume. \since 0.4 */ -pa_volume_t pa_sw_volume_from_dB(double f); - -/** Convert a volume to a decibel value. \since 0.4 */ -double pa_sw_volume_to_dB(pa_volume_t v); - -/** Convert a linear factor to a volume. \since 0.8 */ -pa_volume_t pa_sw_volume_from_linear(double v); - -/** Convert a volume to a linear factor. \since 0.8 */ -double pa_sw_volume_to_linear(pa_volume_t v); - -#ifdef INFINITY -#define PA_DECIBEL_MININFTY (-INFINITY) -#else -/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). \since 0.4 */ -#define PA_DECIBEL_MININFTY (-200) -#endif - -PA_C_DECL_END - -#endif diff --git a/polyp/winsock.h b/polyp/winsock.h deleted file mode 100644 index b1e0f7d4..00000000 --- a/polyp/winsock.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef foowinsockhfoo -#define foowinsockhfoo - -#ifdef HAVE_WINSOCK2_H -#include - -#define ESHUTDOWN WSAESHUTDOWN -#define ECONNRESET WSAECONNRESET -#define ECONNABORTED WSAECONNABORTED -#define ENETRESET WSAENETRESET -#define EINPROGRESS WSAEINPROGRESS -#define EAFNOSUPPORT WSAEAFNOSUPPORT -#define ETIMEDOUT WSAETIMEDOUT -#define ECONNREFUSED WSAECONNREFUSED -#define EHOSTUNREACH WSAEHOSTUNREACH - -#endif - -#ifdef HAVE_WS2TCPIP_H -#include -#endif - -#endif diff --git a/polyp/x11prop.c b/polyp/x11prop.c deleted file mode 100644 index e57fc136..00000000 --- a/polyp/x11prop.c +++ /dev/null @@ -1,70 +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 -#endif - -#include - -#include -#include - -#include "x11prop.h" - - -void pa_x11_set_prop(Display *d, const char *name, const char *data) { - Atom a = XInternAtom(d, name, False); - XChangeProperty(d, RootWindow(d, 0), a, XA_STRING, 8, PropModeReplace, (const unsigned char*) data, strlen(data)+1); -} - -void pa_x11_del_prop(Display *d, const char *name) { - Atom a = XInternAtom(d, name, False); - XDeleteProperty(d, RootWindow(d, 0), a); -} - -char* pa_x11_get_prop(Display *d, const char *name, char *p, size_t l) { - Atom actual_type; - int actual_format; - unsigned long nitems; - unsigned long nbytes_after; - unsigned char *prop = NULL; - char *ret = NULL; - - Atom a = XInternAtom(d, name, False); - if (XGetWindowProperty(d, RootWindow(d, 0), a, 0, (l+2)/4, False, XA_STRING, &actual_type, &actual_format, &nitems, &nbytes_after, &prop) != Success) - goto finish; - - if (actual_type != XA_STRING) - goto finish; - - memcpy(p, prop, nitems); - p[nitems] = 0; - - ret = p; - -finish: - - if (prop) - XFree(prop); - - return ret; -} diff --git a/polyp/x11prop.h b/polyp/x11prop.h deleted file mode 100644 index 5531c640..00000000 --- a/polyp/x11prop.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef foox11prophfoo -#define foox11prophfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -#include - -void pa_x11_set_prop(Display *d, const char *name, const char *data); -void pa_x11_del_prop(Display *d, const char *name); -char* pa_x11_get_prop(Display *d, const char *name, char *p, size_t l); - -#endif diff --git a/polyp/x11wrap.c b/polyp/x11wrap.c deleted file mode 100644 index e20a50a6..00000000 --- a/polyp/x11wrap.c +++ /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 -#include - -#include "llist.h" -#include "x11wrap.h" -#include "xmalloc.h" -#include "log.h" -#include "props.h" - -typedef struct pa_x11_internal pa_x11_internal; - -struct pa_x11_internal { - PA_LLIST_FIELDS(pa_x11_internal); - pa_x11_wrapper *wrapper; - pa_io_event* io_event; - int fd; -}; - -struct pa_x11_wrapper { - pa_core *core; - int ref; - - char *property_name; - Display *display; - - pa_defer_event* defer_event; - pa_io_event* io_event; - - PA_LLIST_HEAD(pa_x11_client, clients); - PA_LLIST_HEAD(pa_x11_internal, internals); -}; - -struct pa_x11_client { - PA_LLIST_FIELDS(pa_x11_client); - pa_x11_wrapper *wrapper; - int (*callback)(pa_x11_wrapper *w, XEvent *e, void *userdata); - void *userdata; -}; - -/* Dispatch all pending X11 events */ -static void work(pa_x11_wrapper *w) { - assert(w && w->ref >= 1); - - while (XPending(w->display)) { - pa_x11_client *c; - XEvent e; - XNextEvent(w->display, &e); - - for (c = w->clients; c; c = c->next) { - assert(c->callback); - if (c->callback(w, &e, c->userdata) != 0) - break; - } - } -} - -/* IO notification event for the X11 display connection */ -static void display_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { - pa_x11_wrapper *w = userdata; - assert(m && e && fd >= 0 && w && w->ref >= 1); - work(w); -} - -/* Deferred notification event. Called once each main loop iteration */ -static void defer_event(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { - pa_x11_wrapper *w = userdata; - assert(m && e && w && w->ref >= 1); - work(w); -} - -/* IO notification event for X11 internal connections */ -static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { - pa_x11_wrapper *w = userdata; - assert(m && e && fd >= 0 && w && w->ref >= 1); - - XProcessInternalConnection(w->display, fd); -} - -/* Add a new IO source for the specified X11 internal connection */ -static pa_x11_internal* x11_internal_add(pa_x11_wrapper *w, int fd) { - pa_x11_internal *i; - assert(i && fd >= 0); - - i = pa_xmalloc(sizeof(pa_x11_internal)); - i->wrapper = w; - i->io_event = w->core->mainloop->io_new(w->core->mainloop, fd, PA_IO_EVENT_INPUT, internal_io_event, w); - i->fd = fd; - - PA_LLIST_PREPEND(pa_x11_internal, w->internals, i); - return i; -} - -/* Remove an IO source for an X11 internal connection */ -static void x11_internal_remove(pa_x11_wrapper *w, pa_x11_internal *i) { - assert(i); - - PA_LLIST_REMOVE(pa_x11_internal, w->internals, i); - w->core->mainloop->io_free(i->io_event); - pa_xfree(i); -} - -/* Implementation of XConnectionWatchProc */ -static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening, XPointer *watch_data) { - pa_x11_wrapper *w = (pa_x11_wrapper*) userdata; - assert(display && w && fd >= 0); - - if (opening) - *watch_data = (XPointer) x11_internal_add(w, fd); - else - x11_internal_remove(w, (pa_x11_internal*) *watch_data); -} - -static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char *t) { - pa_x11_wrapper*w; - Display *d; - int r; - - if (!(d = XOpenDisplay(name))) { - pa_log(__FILE__": XOpenDisplay() failed\n"); - return NULL; - } - - w = pa_xmalloc(sizeof(pa_x11_wrapper)); - w->core = c; - w->ref = 1; - w->property_name = pa_xstrdup(t); - w->display = d; - - PA_LLIST_HEAD_INIT(pa_x11_client, w->clients); - PA_LLIST_HEAD_INIT(pa_x11_internal, w->internals); - - w->defer_event = c->mainloop->defer_new(c->mainloop, defer_event, w); - w->io_event = c->mainloop->io_new(c->mainloop, ConnectionNumber(d), PA_IO_EVENT_INPUT, display_io_event, w); - - XAddConnectionWatch(d, x11_watch, (XPointer) w); - - r = pa_property_set(c, w->property_name, w); - assert(r >= 0); - - return w; -} - -static void x11_wrapper_free(pa_x11_wrapper*w) { - int r; - assert(w); - - r = pa_property_remove(w->core, w->property_name); - assert(r >= 0); - - assert(!w->clients); - - XRemoveConnectionWatch(w->display, x11_watch, (XPointer) w); - XCloseDisplay(w->display); - - w->core->mainloop->io_free(w->io_event); - w->core->mainloop->defer_free(w->defer_event); - - while (w->internals) - x11_internal_remove(w, w->internals); - - pa_xfree(w->property_name); - pa_xfree(w); -} - -pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name) { - char t[256]; - pa_x11_wrapper *w; - assert(c); - - snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "-" : "", name ? name : ""); - if ((w = pa_property_get(c, t))) - return pa_x11_wrapper_ref(w); - - return x11_wrapper_new(c, name, t); -} - -pa_x11_wrapper* pa_x11_wrapper_ref(pa_x11_wrapper *w) { - assert(w && w->ref >= 1); - w->ref++; - return w; -} - -void pa_x11_wrapper_unref(pa_x11_wrapper* w) { - assert(w && w->ref >= 1); - - if (!(--w->ref)) - x11_wrapper_free(w); -} - -Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w) { - assert(w && w->ref >= 1); - return w->display; -} - -pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w, XEvent *e, void *userdata), void *userdata) { - pa_x11_client *c; - assert(w && w->ref >= 1); - - c = pa_xmalloc(sizeof(pa_x11_client)); - c->wrapper = w; - c->callback = cb; - c->userdata = userdata; - - PA_LLIST_PREPEND(pa_x11_client, w->clients, c); - - return c; -} - -void pa_x11_client_free(pa_x11_client *c) { - assert(c && c->wrapper && c->wrapper->ref >= 1); - - PA_LLIST_REMOVE(pa_x11_client, c->wrapper->clients, c); - pa_xfree(c); -} diff --git a/polyp/x11wrap.h b/polyp/x11wrap.h deleted file mode 100644 index 15645168..00000000 --- a/polyp/x11wrap.h +++ /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 - -#include "core.h" - -typedef struct pa_x11_wrapper pa_x11_wrapper; - -/* Return the X11 wrapper for this core. In case no wrapper was - existant before, allocate a new one */ -pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name); - -/* Increase the wrapper's reference count by one */ -pa_x11_wrapper* pa_x11_wrapper_ref(pa_x11_wrapper *w); - -/* Decrease the reference counter of an X11 wrapper object */ -void pa_x11_wrapper_unref(pa_x11_wrapper* w); - -/* Return the X11 display object for this connection */ -Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w); - -typedef struct pa_x11_client pa_x11_client; - -/* Register an X11 client, that is called for each X11 event */ -pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w, XEvent *e, void *userdata), void *userdata); - -/* Free an X11 client object */ -void pa_x11_client_free(pa_x11_client *c); - -#endif diff --git a/polyp/xmalloc.c b/polyp/xmalloc.c deleted file mode 100644 index bf366347..00000000 --- a/polyp/xmalloc.c +++ /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 -#endif - -#include -#include -#include -#include - -#include "memory.h" -#include "util.h" -#include "xmalloc.h" -#include "gccmacro.h" - -/* Make sure not to allocate more than this much memory. */ -#define MAX_ALLOC_SIZE (1024*1024*20) /* 20MB */ - -/* #undef malloc */ -/* #undef free */ -/* #undef realloc */ -/* #undef strndup */ -/* #undef strdup */ - -static void oom(void) PA_GCC_NORETURN; - -/** called in case of an OOM situation. Prints an error message and - * exits */ -static void oom(void) { - static const char e[] = "Not enough memory\n"; - pa_loop_write(STDERR_FILENO, e, sizeof(e)-1); -#ifdef SIGQUIT - raise(SIGQUIT); -#endif - _exit(1); -} - -void* pa_xmalloc(size_t size) { - void *p; - assert(size > 0); - assert(size < MAX_ALLOC_SIZE); - - if (!(p = malloc(size))) - oom(); - - return p; -} - -void* pa_xmalloc0(size_t size) { - void *p; - assert(size > 0); - assert(size < MAX_ALLOC_SIZE); - - if (!(p = calloc(1, size))) - oom(); - - return p; -} - -void *pa_xrealloc(void *ptr, size_t size) { - void *p; - assert(size > 0); - assert(size < MAX_ALLOC_SIZE); - - if (!(p = realloc(ptr, size))) - oom(); - return p; -} - -void* pa_xmemdup(const void *p, size_t l) { - if (!p) - return NULL; - else { - char *r = pa_xmalloc(l); - memcpy(r, p, l); - return r; - } -} - -char *pa_xstrdup(const char *s) { - if (!s) - return NULL; - - return pa_xmemdup(s, strlen(s)+1); -} - -char *pa_xstrndup(const char *s, size_t l) { - if (!s) - return NULL; - else { - char *r; - size_t t = strlen(s); - - if (t > l) - t = l; - - r = pa_xmemdup(s, t+1); - r[t] = 0; - return r; - } -} - diff --git a/polyp/xmalloc.h b/polyp/xmalloc.h deleted file mode 100644 index 2946011a..00000000 --- a/polyp/xmalloc.h +++ /dev/null @@ -1,58 +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 -#include -#include -#include - -void* pa_xmalloc(size_t l); -void *pa_xmalloc0(size_t l); -void *pa_xrealloc(void *ptr, size_t size); -#define pa_xfree free - -char *pa_xstrdup(const char *s); -char *pa_xstrndup(const char *s, size_t l); - -void* pa_xmemdup(const void *p, size_t l); - -/** Internal helper for pa_xnew() */ -static inline void* pa_xnew_internal(unsigned n, size_t k) { - assert(n < INT_MAX/k); - return pa_xmalloc(n*k); -} - -/** Allocate n new structures of the specified type. */ -#define pa_xnew(type, n) ((type*) pa_xnew_internal((n), sizeof(type))) - -/** Internal helper for pa_xnew0() */ -static inline void* pa_xnew0_internal(unsigned n, size_t k) { - assert(n < INT_MAX/k); - return pa_xmalloc0(n*k); -} - -/** Same as pa_xnew() but set the memory to zero */ -#define pa_xnew0(type, n) ((type*) pa_xnew0_internal((n), sizeof(type))) - -#endif diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..92c7dae1 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,1061 @@ +# $Id$ +# +# This file is part of polypaudio. +# +# polypaudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# polypaudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with polypaudio; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + + +################################### +# Extra directories # +################################### + +polypincludedir=$(includedir)/polyp +polypcoreincludedir=$(includedir)/polypcore +polypconfdir=$(sysconfdir)/polypaudio + +modlibdir=$(libdir)/polypaudio + +################################### +# Defines # +################################### + +POLYPAUDIO_BINARY=$(bindir)/polypaudio$(EXEEXT) +if OS_IS_WIN32 +DEFAULT_CONFIG_DIR=%POLYP_ROOT% +else +DEFAULT_CONFIG_DIR=$(polypconfdir) +endif + +################################### +# Compiler/linker flags # +################################### + +AM_CFLAGS = -I$(top_srcdir)/src +AM_CFLAGS += $(PTHREAD_CFLAGS) -D_POSIX_PTHREAD_SEMANTICS +AM_CFLAGS += $(LTDLINCL) +AM_CFLAGS += $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) +AM_CFLAGS += -DDLSEARCHPATH=\"$(modlibdir)\" +#AM_CFLAGS += -DDLSEARCHPATH=\"$(shell pwd)\" +AM_CFLAGS += -DDEFAULT_CONFIG_DIR=\"$(DEFAULT_CONFIG_DIR)\" +AM_CFLAGS += -DPOLYPAUDIO_BINARY=\"$(POLYPAUDIO_BINARY)\" + +# This cool debug trap works on i386/gcc only +AM_CFLAGS += '-DDEBUG_TRAP=__asm__("int $$3")' + +AM_LIBADD = $(PTHREAD_LIBS) +AM_LDADD = $(PTHREAD_LIBS) + +# Only required on some platforms but defined for all to avoid errors +AM_LDFLAGS = -no-undefined + +if STATIC_BINS +BINLDFLAGS = -static +endif + +if OS_IS_WIN32 +AM_LDFLAGS+=-Wl,--export-all-symbols +WINSOCK_LIBS=-lwsock32 -lws2_32 -lwininet +endif + +################################### +# Extra files # +################################### + +EXTRA_DIST = \ + client.conf.in \ + daemon.conf.in \ + default.pa.in \ + depmod.py \ + utils/esdcompat.sh.in \ + modules/module-defs.h.m4 + +polypconf_DATA = default.pa daemon.conf client.conf + +BUILT_SOURCES = polyp/polyplib-version.h + +################################### +# Main daemon # +################################### + +bin_PROGRAMS = polypaudio + +polypaudio_SOURCES = \ + daemon/caps.h daemon/caps.c \ + daemon/cmdline.c daemon/cmdline.h \ + daemon/cpulimit.c daemon/cpulimit.h \ + daemon/daemon-conf.c daemon/daemon-conf.h \ + daemon/dumpmodules.c daemon/dumpmodules.h \ + daemon/main.c \ + polypcore/gccmacro.h + +polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) +polypaudio_CPPFLAGS = $(AM_CPPFLAGS) +polypaudio_LDADD = $(AM_LDADD) libpolypcore.la $(LIBLTDL) \ + $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS) +polypaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -dlopen force $(foreach f,$(PREOPEN_LIBS),-dlopen $(f)) + +if PREOPEN_MODS +PREOPEN_LIBS = $(PREOPEN_MODS) +else +PREOPEN_LIBS = $(modlib_LTLIBRARIES) +endif + +################################### +# Utility programs # +################################### + +bin_PROGRAMS += \ + pacat \ + pactl \ + paplay + +if HAVE_AF_UNIX +bin_PROGRAMS += pacmd +endif + +if HAVE_X11 +bin_PROGRAMS += pax11publish +endif + +if HAVE_HOWL +bin_PROGRAMS += pabrowse +endif + +bin_SCRIPTS = utils/esdcompat.sh + +pacat_SOURCES = utils/pacat.c +pacat_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la +pacat_CFLAGS = $(AM_CFLAGS) +pacat_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +paplay_SOURCES = utils/paplay.c +paplay_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la $(LIBSNDFILE_LIBS) +paplay_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS) +paplay_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +pactl_SOURCES = utils/pactl.c +pactl_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la $(LIBSNDFILE_LIBS) +pactl_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS) +pactl_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +pacmd_SOURCES = utils/pacmd.c polypcore/pid.c polypcore/pid.h +pacmd_CFLAGS = $(AM_CFLAGS) +pacmd_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la +pacmd_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +pax11publish_SOURCES = utils/pax11publish.c +pax11publish_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS) +pax11publish_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) +pax11publish_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +pabrowse_SOURCES = utils/pabrowse.c +pabrowse_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la libpolyp-browse-@PA_MAJORMINOR@.la +pabrowse_CFLAGS = $(AM_CFLAGS) +pabrowse_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +################################### +# Test programs # +################################### + +noinst_PROGRAMS = \ + mainloop-test \ + mcalign-test \ + pacat-simple \ + parec-simple \ + strlist-test \ + voltest + +if HAVE_SIGXCPU +noinst_PROGRAMS += \ + cpulimit-test \ + cpulimit-test2 +endif + +if HAVE_GLIB20 +noinst_PROGRAMS += \ + mainloop-test-glib +endif + +if HAVE_GLIB12 +noinst_PROGRAMS += \ + mainloop-test-glib12 +endif + +mainloop_test_SOURCES = tests/mainloop-test.c +mainloop_test_CFLAGS = $(AM_CFLAGS) +mainloop_test_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la libpolyp-@PA_MAJORMINOR@.la +mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +mcalign_test_SOURCES = tests/mcalign-test.c +mcalign_test_CFLAGS = $(AM_CFLAGS) +mcalign_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpolypcore.la +mcalign_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +pacat_simple_SOURCES = tests/pacat-simple.c +pacat_simple_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-simple-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la +pacat_simple_CFLAGS = $(AM_CFLAGS) +pacat_simple_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +parec_simple_SOURCES = tests/parec-simple.c +parec_simple_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-simple-@PA_MAJORMINOR@.la libpolyp-error-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la +parec_simple_CFLAGS = $(AM_CFLAGS) +parec_simple_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +strlist_test_SOURCES = tests/strlist-test.c +strlist_test_CFLAGS = $(AM_CFLAGS) +strlist_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpolypcore.la libstrlist.la +strlist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +voltest_SOURCES = tests/voltest.c +voltest_CFLAGS = $(AM_CFLAGS) +voltest_LDADD = $(AM_LDADD) libpolyp-@PA_MAJORMINOR@.la +voltest_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +cpulimit_test_SOURCES = tests/cpulimit-test.c daemon/cpulimit.c daemon/cpulimit.h +cpulimit_test_CFLAGS = $(AM_CFLAGS) +cpulimit_test_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la libpolypcore.la +cpulimit_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +cpulimit_test2_SOURCES = tests/cpulimit-test.c daemon/cpulimit.c daemon/cpulimit.h +cpulimit_test2_CFLAGS = $(AM_CFLAGS) -DTEST2 +cpulimit_test2_LDADD = $(AM_LDADD) libpolyp-mainloop-@PA_MAJORMINOR@.la libpolypcore.la +cpulimit_test2_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +mainloop_test_glib_SOURCES = $(mainloop_test_SOURCES) +mainloop_test_glib_CFLAGS = $(mainloop_test_CFLAGS) $(GLIB20_CFLAGS) -DGLIB_MAIN_LOOP +mainloop_test_glib_LDADD = $(mainloop_test_LDADD) $(GLIB20_LIBS) libpolyp-mainloop-glib-@PA_MAJORMINOR@.la +mainloop_test_glib_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +mainloop_test_glib12_SOURCES = $(mainloop_test_SOURCES) +mainloop_test_glib12_CFLAGS = $(mainloop_test_CFLAGS) $(GLIB12_CFLAGS) -DGLIB_MAIN_LOOP +mainloop_test_glib12_LDADD = $(mainloop_test_LDADD) $(GLIB12_LIBS) libpolyp-mainloop-glib12-@PA_MAJORMINOR@.la +mainloop_test_glib12_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + +################################### +# Client library # +################################### + +polypinclude_HEADERS = \ + polyp/cdecl.h \ + polyp/channelmap.h \ + polyp/glib-mainloop.h \ + polyp/mainloop.h \ + polyp/mainloop-api.h \ + polyp/mainloop-signal.h \ + polyp/polyplib.h \ + polyp/polyplib-context.h \ + polyp/polyplib-def.h \ + polyp/polyplib-error.h \ + polyp/polyplib-introspect.h \ + polyp/polyplib-operation.h \ + polyp/polyplib-scache.h \ + polyp/polyplib-simple.h \ + polyp/polyplib-stream.h \ + polyp/polyplib-subscribe.h \ + polyp/polyplib-version.h \ + polyp/sample.h \ + polyp/volume.h + +if HAVE_HOWL +polypinclude_HEADERS += \ + polyp/polyplib-browser.h +endif + +lib_LTLIBRARIES = \ + libpolyp-@PA_MAJORMINOR@.la \ + libpolyp-error-@PA_MAJORMINOR@.la \ + libpolyp-mainloop-@PA_MAJORMINOR@.la \ + libpolyp-simple-@PA_MAJORMINOR@.la + +if HAVE_HOWL +lib_LTLIBRARIES += \ + libpolyp-browse-@PA_MAJORMINOR@.la +endif + +if HAVE_GLIB20 +lib_LTLIBRARIES += \ + libpolyp-mainloop-glib-@PA_MAJORMINOR@.la +endif + +if HAVE_GLIB12 +lib_LTLIBRARIES += \ + libpolyp-mainloop-glib12-@PA_MAJORMINOR@.la +endif + +# Public interface +libpolyp_@PA_MAJORMINOR@_la_SOURCES = \ + polyp/cdecl.h \ + polyp/channelmap.c polyp/channelmap.h \ + polyp/client-conf.c polyp/client-conf.h \ + polyp/llist.h \ + polyp/mainloop-api.c polyp/mainloop-api.h \ + polyp/polyplib.h \ + polyp/polyplib-context.c polyp/polyplib-context.h \ + polyp/polyplib-def.h \ + polyp/polyplib-internal.h \ + polyp/polyplib-introspect.c polyp/polyplib-introspect.h \ + polyp/polyplib-operation.c polyp/polyplib-operation.h \ + polyp/polyplib-scache.c polyp/polyplib-scache.h \ + polyp/polyplib-stream.c polyp/polyplib-stream.h \ + polyp/polyplib-subscribe.c polyp/polyplib-subscribe.h \ + polyp/sample.c polyp/sample.h \ + polyp/volume.c polyp/volume.h + +# Internal stuff that is shared with libpolypcore +libpolyp_@PA_MAJORMINOR@_la_SOURCES += \ + polypcore/authkey.c polypcore/authkey.h \ + polypcore/conf-parser.c polypcore/conf-parser.h \ + polypcore/dllmain.c \ + polypcore/dynarray.c polypcore/dynarray.h \ + polypcore/gccmacro.h \ + polypcore/idxset.c polypcore/idxset.h \ + polypcore/iochannel.c polypcore/iochannel.h \ + polypcore/log.c polypcore/log.h \ + polypcore/mcalign.c polypcore/mcalign.h \ + polypcore/memblock.c polypcore/memblock.h \ + polypcore/memchunk.c polypcore/memchunk.h \ + polypcore/native-common.h \ + polypcore/packet.c polypcore/packet.h \ + polypcore/parseaddr.c polypcore/parseaddr.h \ + polypcore/pdispatch.c polypcore/pdispatch.h \ + polypcore/pstream.c polypcore/pstream.h \ + polypcore/pstream-util.c polypcore/pstream-util.h \ + polypcore/queue.c polypcore/queue.h \ + polypcore/random.c polypcore/random.h \ + polypcore/socket-client.c polypcore/socket-client.h \ + polypcore/socket-util.c polypcore/socket-util.h \ + polypcore/strbuf.c polypcore/strbuf.h \ + polypcore/strlist.c polypcore/strlist.h \ + polypcore/tagstruct.c polypcore/tagstruct.h \ + polypcore/util.c polypcore/util.h \ + polypcore/winsock.h \ + polypcore/xmalloc.c polypcore/xmalloc.h + +if HAVE_X11 +libpolyp_@PA_MAJORMINOR@_la_SOURCES += \ + polyp/client-conf-x11.c polyp/client-conf-x11.h \ + polypcore/x11prop.c polypcore/x11prop.h +endif + +libpolyp_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) +libpolyp_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 +libpolyp_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) + +if HAVE_X11 +libpolyp_@PA_MAJORMINOR@_la_CFLAGS += $(X_CFLAGS) +libpolyp_@PA_MAJORMINOR@_la_LDFLAGS += $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) +endif + +if HAVE_LIBASYNCNS +libpolyp_@PA_MAJORMINOR@_la_CFLAGS += $(LIBASYNCNS_CFLAGS) +libpolyp_@PA_MAJORMINOR@_la_LIBADD += $(LIBASYNCNS_LIBS) +endif + +libpolyp_error_@PA_MAJORMINOR@_la_SOURCES = polyp/polyplib-error.c polyp/polyplib-error.h +libpolyp_error_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) +libpolyp_error_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la +libpolyp_error_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 + +libpolyp_mainloop_@PA_MAJORMINOR@_la_SOURCES = \ + polyp/mainloop.c polyp/mainloop.h \ + polyp/mainloop-signal.c polyp/mainloop-signal.h \ + polypcore/poll.c polypcore/poll.h +libpolyp_mainloop_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) +libpolyp_mainloop_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la $(WINSOCK_LIBS) +libpolyp_mainloop_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 + +libpolyp_simple_@PA_MAJORMINOR@_la_SOURCES = polyp/polyplib-simple.c polyp/polyplib-simple.h +libpolyp_simple_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) +libpolyp_simple_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la libpolyp-mainloop-@PA_MAJORMINOR@.la +libpolyp_simple_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 + +libpolyp_browse_@PA_MAJORMINOR@_la_SOURCES = polyp/polyplib-browser.c polyp/polyplib-browser.h +libpolyp_browse_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(HOWL_CFLAGS) +libpolyp_browse_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-@PA_MAJORMINOR@.la $(HOWL_LIBS) +libpolyp_browse_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 + +libpolyp_mainloop_glib_@PA_MAJORMINOR@_la_SOURCES = polyp/glib-mainloop.h polyp/glib-mainloop.c +libpolyp_mainloop_glib_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(GLIB20_CFLAGS) +libpolyp_mainloop_glib_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-mainloop-@PA_MAJORMINOR@.la $(GLIB20_LIBS) +libpolyp_mainloop_glib_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 + +libpolyp_mainloop_glib12_@PA_MAJORMINOR@_la_SOURCES = polyp/glib-mainloop.h polyp/glib12-mainloop.c +libpolyp_mainloop_glib12_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(GLIB12_CFLAGS) +libpolyp_mainloop_glib12_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) libpolyp-mainloop-@PA_MAJORMINOR@.la $(GLIB12_LIBS) +libpolyp_mainloop_glib12_@PA_MAJORMINOR@_la_LDFLAGS = -version-info 0:0:0 + +################################### +# Daemon core library # +################################### + +polypcoreinclude_HEADERS = \ + polypcore/cli-command.h \ + polypcore/client.h \ + polypcore/core.h \ + polypcore/dynarray.h \ + polypcore/endianmacros.h \ + polypcore/hashmap.h \ + polypcore/idxset.h \ + polypcore/iochannel.h \ + polypcore/log.h \ + polypcore/memblock.h \ + polypcore/memblockq.h \ + polypcore/memchunk.h \ + polypcore/modargs.h \ + polypcore/module.h \ + polypcore/namereg.h \ + polypcore/queue.h \ + polypcore/resampler.h \ + polypcore/sample-util.h \ + polypcore/sink.h \ + polypcore/sink-input.h \ + polypcore/sioman.h \ + polypcore/socket-server.h \ + polypcore/socket-client.h \ + polypcore/socket-util.h \ + polypcore/source.h \ + polypcore/source-output.h \ + polypcore/strbuf.h \ + polypcore/tokenizer.h \ + polypcore/tagstruct.h \ + polypcore/util.h + +lib_LTLIBRARIES += libpolypcore.la + +# Some public stuff is used even in the core. +libpolypcore_la_SOURCES = \ + polyp/channelmap.c polyp/channelmap.h \ + polyp/mainloop.c polyp/mainloop.h \ + polyp/mainloop-api.c polyp/mainloop-api.h \ + polyp/mainloop-signal.c polyp/mainloop-signal.h \ + polyp/sample.c polyp/sample.h \ + polyp/volume.c polyp/volume.h + +# Pure core stuff (some are shared in libpolyp though). +libpolypcore_la_SOURCES += \ + polypcore/autoload.c polypcore/autoload.h \ + polypcore/cli-command.c polypcore/cli-command.h \ + polypcore/cli-text.c polypcore/cli-text.h \ + polypcore/client.c polypcore/client.h \ + polypcore/conf-parser.c polypcore/conf-parser.h \ + polypcore/core.c polypcore/core.h \ + polypcore/dllmain.c \ + polypcore/dynarray.c polypcore/dynarray.h \ + polypcore/endianmacros.h \ + polypcore/g711.c polypcore/g711.h \ + polypcore/hashmap.c polypcore/hashmap.h \ + polypcore/idxset.c polypcore/idxset.h \ + polypcore/log.c polypcore/log.h \ + polypcore/mcalign.c polypcore/mcalign.h \ + polypcore/memblock.c polypcore/memblock.h \ + polypcore/memblockq.c polypcore/memblockq.h \ + polypcore/memchunk.c polypcore/memchunk.h \ + polypcore/modargs.c polypcore/modargs.h \ + polypcore/modinfo.c polypcore/modinfo.h \ + polypcore/module.c polypcore/module.h \ + polypcore/namereg.c polypcore/namereg.h \ + polypcore/pid.c polypcore/pid.h \ + polypcore/play-memchunk.c polypcore/play-memchunk.h \ + polypcore/poll.c polypcore/poll.h \ + polypcore/props.c polypcore/props.h \ + polypcore/queue.c polypcore/queue.h \ + polypcore/random.c polypcore/random.h \ + polypcore/resampler.c polypcore/resampler.h \ + polypcore/sample-util.c polypcore/sample-util.h \ + polypcore/scache.c polypcore/scache.h \ + polypcore/sconv.c polypcore/sconv.h \ + polypcore/sconv-s16be.c polypcore/sconv-s16be.h \ + polypcore/sconv-s16le.c polypcore/sconv-s16le.h \ + polypcore/sink.c polypcore/sink.h \ + polypcore/sink-input.c polypcore/sink-input.h \ + polypcore/sioman.c polypcore/sioman.h \ + polypcore/sound-file.c polypcore/sound-file.h \ + polypcore/sound-file-stream.c polypcore/sound-file-stream.h \ + polypcore/source.c polypcore/source.h \ + polypcore/source-output.c polypcore/source-output.h \ + polypcore/strbuf.c polypcore/strbuf.h \ + polypcore/subscribe.c polypcore/subscribe.h \ + polypcore/tokenizer.c polypcore/tokenizer.h \ + polypcore/util.c polypcore/util.h \ + polypcore/winsock.h \ + polypcore/xmalloc.c polypcore/xmalloc.h + +libpolypcore_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBOIL_CFLAGS) +libpolypcore_la_LDFLAGS = -avoid-version +libpolypcore_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(WINSOCK_LIBS) $(LIBOIL_LIBS) + +################################### +# Plug-in support libraries # +################################### + +### Warning! Due to an obscure bug in libtool/automake it is required +### that the libraries in modlib_LTLIBRARIES are specified in-order, +### i.e. libraries near the end of the list depend on libraries near +### the head, and not the other way! + +modlib_LTLIBRARIES = \ + libsocket-util.la \ + libiochannel.la \ + libsocket-server.la \ + libsocket-client.la \ + libparseaddr.la \ + libpacket.la \ + libpstream.la \ + libioline.la \ + libcli.la \ + libprotocol-cli.la \ + libtagstruct.la \ + libpstream-util.la \ + libpdispatch.la \ + libauthkey.la \ + libauthkey-prop.la \ + libstrlist.la \ + libprotocol-simple.la \ + libprotocol-esound.la \ + libprotocol-native.la \ + libprotocol-http.la + +if HAVE_X11 +modlib_LTLIBRARIES += \ + libx11wrap.la \ + libx11prop.la +endif + +if HAVE_OSS +modlib_LTLIBRARIES += \ + liboss-util.la +endif + +if HAVE_ALSA +modlib_LTLIBRARIES += \ + libalsa-util.la +endif + +if HAVE_HOWL +modlib_LTLIBRARIES += \ + libhowl-wrap.la +endif + +libprotocol_simple_la_SOURCES = polypcore/protocol-simple.c polypcore/protocol-simple.h +libprotocol_simple_la_LDFLAGS = -avoid-version +libprotocol_simple_la_LIBADD = $(AM_LIBADD) libpolypcore.la libsocket-server.la libiochannel.la + +libsocket_server_la_SOURCES = \ + polypcore/inet_ntop.c polypcore/inet_ntop.h \ + polypcore/socket-server.c polypcore/socket-server.h +libsocket_server_la_LDFLAGS = -avoid-version +libsocket_server_la_LIBADD = $(AM_LIBADD) libpolypcore.la libiochannel.la libsocket-util.la $(LIBWRAP_LIBS) $(WINSOCK_LIBS) + +libsocket_client_la_SOURCES = polypcore/socket-client.c polypcore/socket-client.h +libsocket_client_la_LDFLAGS = -avoid-version +libsocket_client_la_LIBADD = $(AM_LIBADD) libpolypcore.la libiochannel.la libsocket-util.la libparseaddr.la $(LIBASYNCNS_LIBS) $(WINSOCK_LIBS) +libsocket_client_la_CFLAGS = $(AM_CFLAGS) $(LIBASYNCNS_CFLAGS) + +libparseaddr_la_SOURCES = polypcore/parseaddr.c polypcore/parseaddr.h +libparseaddr_la_LDFLAGS = -avoid-version +libparseaddr_la_LIBADD = $(AM_LIBADD) libpolypcore.la + +libpstream_la_SOURCES = polypcore/pstream.c polypcore/pstream.h +libpstream_la_LDFLAGS = -avoid-version +libpstream_la_LIBADD = $(AM_LIBADD) libpolypcore.la libpacket.la libiochannel.la $(WINSOCK_LIBS) + +libpstream_util_la_SOURCES = polypcore/pstream-util.c polypcore/pstream-util.h +libpstream_util_la_LDFLAGS = -avoid-version +libpstream_util_la_LIBADD = $(AM_LIBADD) libpacket.la libpstream.la libtagstruct.la + +libpdispatch_la_SOURCES = polypcore/pdispatch.c polypcore/pdispatch.h +libpdispatch_la_LDFLAGS = -avoid-version +libpdispatch_la_LIBADD = $(AM_LIBADD) libtagstruct.la libpolypcore.la + +libiochannel_la_SOURCES = polypcore/iochannel.c polypcore/iochannel.h +libiochannel_la_LDFLAGS = -avoid-version +libiochannel_la_LIBADD = $(AM_LIBADD) libsocket-util.la libpolypcore.la $(WINSOCK_LIBS) + +libpacket_la_SOURCES = polypcore/packet.c polypcore/packet.h +libpacket_la_LDFLAGS = -avoid-version +libpacket_la_LIBADD = $(AM_LIBADD) libpolypcore.la + +libioline_la_SOURCES = polypcore/ioline.c polypcore/ioline.h +libioline_la_LDFLAGS = -avoid-version +libioline_la_LIBADD = $(AM_LIBADD) libiochannel.la libpolypcore.la + +libcli_la_SOURCES = polypcore/cli.c polypcore/cli.h +libcli_la_CPPFLAGS = $(AM_CPPFLAGS) +libcli_la_LDFLAGS = -avoid-version +libcli_la_LIBADD = $(AM_LIBADD) libiochannel.la libioline.la libpolypcore.la + +libstrlist_la_SOURCES = polypcore/strlist.c polypcore/strlist.h +libstrlist_la_LDFLAGS = -avoid-version +libstrlist_la_LIBADD = $(AM_LIBADD) libpolypcore.la + +libprotocol_cli_la_SOURCES = polypcore/protocol-cli.c polypcore/protocol-cli.h +libprotocol_cli_la_LDFLAGS = -avoid-version +libprotocol_cli_la_LIBADD = $(AM_LIBADD) libsocket-server.la libiochannel.la libcli.la libpolypcore.la + +libprotocol_http_la_SOURCES = polypcore/protocol-http.c polypcore/protocol-http.h +libprotocol_http_la_LDFLAGS = -avoid-version +libprotocol_http_la_LIBADD = $(AM_LIBADD) libsocket-server.la libioline.la libpolypcore.la libiochannel.la + +libprotocol_native_la_SOURCES = polypcore/protocol-native.c polypcore/protocol-native.h polypcore/native-common.h +libprotocol_native_la_LDFLAGS = -avoid-version +libprotocol_native_la_LIBADD = $(AM_LIBADD) libsocket-server.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libstrlist.la libpolypcore.la libiochannel.la + +libtagstruct_la_SOURCES = polypcore/tagstruct.c polypcore/tagstruct.h +libtagstruct_la_LDFLAGS = -avoid-version +libtagstruct_la_LIBADD = $(AM_LIBADD) libpolypcore.la $(WINSOCK_LIBS) + +libprotocol_esound_la_SOURCES = polypcore/protocol-esound.c polypcore/protocol-esound.h polypcore/esound.h +libprotocol_esound_la_LDFLAGS = -avoid-version +libprotocol_esound_la_LIBADD = $(AM_LIBADD) libsocket-server.la libiochannel.la libauthkey.la libpolypcore.la + +libauthkey_la_SOURCES = polypcore/authkey.c polypcore/authkey.h +libauthkey_la_LDFLAGS = -avoid-version +libauthkey_la_LIBADD = $(AM_LIBADD) libpolypcore.la + +libauthkey_prop_la_SOURCES = polypcore/authkey-prop.c polypcore/authkey-prop.h +libauthkey_prop_la_LDFLAGS = -avoid-version +libauthkey_prop_la_LIBADD = $(AM_LIBADD) libpolypcore.la + +libsocket_util_la_SOURCES = polypcore/socket-util.c polypcore/socket-util.h +libsocket_util_la_LDFLAGS = -avoid-version +libsocket_util_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) + +# X11 + +libx11wrap_la_SOURCES = polypcore/x11wrap.c polypcore/x11wrap.h +libx11wrap_la_LDFLAGS = -avoid-version +libx11wrap_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS) +libx11wrap_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) + +libx11prop_la_SOURCES = polypcore/x11prop.c polypcore/x11prop.h +libx11prop_la_LDFLAGS = -avoid-version +libx11prop_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS) +libx11prop_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) + +# OSS + +liboss_util_la_SOURCES = polypcore/oss-util.c polypcore/oss-util.h +liboss_util_la_LDFLAGS = -avoid-version + +# ALSA + +libalsa_util_la_SOURCES = polypcore/alsa-util.c polypcore/alsa-util.h +libalsa_util_la_LDFLAGS = -avoid-version +libalsa_util_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) +libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) + +# HOWL + +libhowl_wrap_la_SOURCES = polypcore/howl-wrap.c polypcore/howl-wrap.h +libhowl_wrap_la_LDFLAGS = -avoid-version +libhowl_wrap_la_LIBADD = $(AM_LIBADD) $(HOWL_LIBS) +libhowl_wrap_la_CFLAGS = $(AM_CFLAGS) $(HOWL_CFLAGS) + +################################### +# Plug-in libraries # +################################### + +modlib_LTLIBRARIES += \ + module-cli.la \ + module-cli-protocol-tcp.la \ + module-cli-protocol-tcp6.la \ + module-simple-protocol-tcp.la \ + module-simple-protocol-tcp6.la \ + module-esound-protocol-tcp.la \ + module-esound-protocol-tcp6.la \ + module-native-protocol-tcp.la \ + module-native-protocol-tcp6.la \ + module-native-protocol-fd.la \ + module-sine.la \ + module-combine.la \ + module-tunnel-sink.la \ + module-tunnel-source.la \ + module-null-sink.la \ + module-esound-sink.la \ + module-http-protocol-tcp.la \ + module-http-protocol-tcp6.la \ + module-detect.la + +if HAVE_AF_UNIX +modlib_LTLIBRARIES += \ + module-cli-protocol-unix.la \ + module-simple-protocol-unix.la \ + module-esound-protocol-unix.la \ + module-native-protocol-unix.la \ + module-http-protocol-unix.la +endif + +if HAVE_MKFIFO +modlib_LTLIBRARIES += \ + module-pipe-sink.la \ + module-pipe-source.la +endif + +if !OS_IS_WIN32 +modlib_LTLIBRARIES += \ + module-esound-compat-spawnfd.la \ + module-esound-compat-spawnpid.la +endif + +if HAVE_REGEX +modlib_LTLIBRARIES += \ + module-match.la +endif + +if HAVE_X11 +modlib_LTLIBRARIES += \ + module-x11-bell.la \ + module-x11-publish.la +endif + +if HAVE_OSS +modlib_LTLIBRARIES += \ + module-oss.la \ + module-oss-mmap.la +endif + +if HAVE_ALSA +modlib_LTLIBRARIES += \ + module-alsa-sink.la \ + module-alsa-source.la +endif + +if HAVE_SOLARIS +modlib_LTLIBRARIES += \ + module-solaris.la +endif + +if HAVE_HOWL +modlib_LTLIBRARIES += \ + module-zeroconf-publish.la +endif + +if HAVE_LIRC +modlib_LTLIBRARIES += \ + module-lirc.la +endif + +if HAVE_EVDEV +modlib_LTLIBRARIES += \ + module-mmkbd-evdev.la +endif + +if OS_IS_WIN32 +modlib_LTLIBRARIES += \ + module-waveout.la +endif + +# These are generated by a M4 script + +SYMDEF_FILES = \ + modules/module-cli-symdef.h \ + modules/module-cli-protocol-tcp-symdef.h \ + modules/module-cli-protocol-tcp6-symdef.h \ + modules/module-cli-protocol-unix-symdef.h \ + modules/module-pipe-sink-symdef.h \ + modules/module-pipe-source-symdef.h \ + modules/module-simple-protocol-tcp-symdef.h \ + modules/module-simple-protocol-tcp6-symdef.h \ + modules/module-simple-protocol-unix-symdef.h \ + modules/module-esound-protocol-tcp-symdef.h \ + modules/module-esound-protocol-tcp6-symdef.h \ + modules/module-esound-protocol-unix-symdef.h \ + modules/module-native-protocol-tcp-symdef.h \ + modules/module-native-protocol-tcp6-symdef.h \ + modules/module-native-protocol-unix-symdef.h \ + modules/module-native-protocol-fd-symdef.h \ + modules/module-sine-symdef.h \ + modules/module-combine-symdef.h \ + modules/module-esound-compat-spawnfd-symdef.h \ + modules/module-esound-compat-spawnpid-symdef.h \ + modules/module-match-symdef.h \ + modules/module-tunnel-sink-symdef.h \ + modules/module-tunnel-source-symdef.h \ + modules/module-null-sink-symdef.h \ + modules/module-esound-sink-symdef.h \ + modules/module-zeroconf-publish-symdef.h \ + modules/module-lirc-symdef.h \ + modules/module-mmkbd-evdev-symdef.h \ + modules/module-http-protocol-tcp-symdef.h \ + modules/module-http-protocol-tcp6-symdef.h \ + modules/module-http-protocol-unix-symdef.h \ + modules/module-x11-bell-symdef.h \ + modules/module-x11-publish-symdef.h \ + modules/module-oss-symdef.h \ + modules/module-oss-mmap-symdef.h \ + modules/module-alsa-sink-symdef.h \ + modules/module-alsa-source-symdef.h \ + modules/module-solaris-symdef.h \ + modules/module-waveout-symdef.h \ + modules/module-detect-symdef.h + +EXTRA_DIST += $(SYMDEF_FILES) +BUILT_SOURCES += $(SYMDEF_FILES) + +$(SYMDEF_FILES): modules/module-defs.h.m4 + $(M4) -Dfname="$@" $< > $@ + +# Simple protocol + +module_simple_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c +module_simple_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS) +module_simple_protocol_tcp_la_LDFLAGS = -module -avoid-version +module_simple_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-simple.la libsocket-server.la + +module_simple_protocol_tcp6_la_SOURCES = modules/module-protocol-stub.c +module_simple_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS) +module_simple_protocol_tcp6_la_LDFLAGS = -module -avoid-version +module_simple_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-simple.la libsocket-server.la + +module_simple_protocol_unix_la_SOURCES = modules/module-protocol-stub.c +module_simple_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS) +module_simple_protocol_unix_la_LDFLAGS = -module -avoid-version +module_simple_protocol_unix_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-simple.la libsocket-server.la libsocket-util.la + +# CLI protocol + +module_cli_la_SOURCES = modules/module-cli.c +module_cli_la_LDFLAGS = -module -avoid-version +module_cli_la_LIBADD = $(AM_LIBADD) libcli.la libiochannel.la libpolypcore.la + +module_cli_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c +module_cli_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS) +module_cli_protocol_tcp_la_LDFLAGS = -module -avoid-version +module_cli_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-cli.la libsocket-server.la + +module_cli_protocol_tcp6_la_SOURCES = modules/module-protocol-stub.c +module_cli_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS) +module_cli_protocol_tcp6_la_LDFLAGS = -module -avoid-version +module_cli_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-cli.la libsocket-server.la + +module_cli_protocol_unix_la_SOURCES = modules/module-protocol-stub.c +module_cli_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS) +module_cli_protocol_unix_la_LDFLAGS = -module -avoid-version +module_cli_protocol_unix_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-cli.la libsocket-server.la libsocket-util.la + +# HTTP protocol + +module_http_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c +module_http_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS) +module_http_protocol_tcp_la_LDFLAGS = -module -avoid-version +module_http_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-http.la libsocket-server.la + +module_http_protocol_tcp6_la_SOURCES = modules/module-protocol-stub.c +module_http_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS) +module_http_protocol_tcp6_la_LDFLAGS = -module -avoid-version +module_http_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-http.la libsocket-server.la + +module_http_protocol_unix_la_SOURCES = modules/module-protocol-stub.c +module_http_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS) +module_http_protocol_unix_la_LDFLAGS = -module -avoid-version +module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-http.la libsocket-server.la libsocket-util.la + +# Native protocol + +module_native_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c +module_native_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS) +module_native_protocol_tcp_la_LDFLAGS = -module -avoid-version +module_native_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-native.la libsocket-server.la + +module_native_protocol_tcp6_la_SOURCES = modules/module-protocol-stub.c +module_native_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS) +module_native_protocol_tcp6_la_LDFLAGS = -module -avoid-version +module_native_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-native.la libsocket-server.la + +module_native_protocol_unix_la_SOURCES = modules/module-protocol-stub.c +module_native_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS) +module_native_protocol_unix_la_LDFLAGS = -module -avoid-version +module_native_protocol_unix_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-native.la libsocket-server.la libsocket-util.la + +module_native_protocol_fd_la_SOURCES = modules/module-native-protocol-fd.c +module_native_protocol_fd_la_CFLAGS = $(AM_CFLAGS) +module_native_protocol_fd_la_LDFLAGS = -module -avoid-version +module_native_protocol_fd_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-native.la libsocket-server.la libsocket-util.la libiochannel.la + +# EsounD protocol + +module_esound_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c +module_esound_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS) +module_esound_protocol_tcp_la_LDFLAGS = -module -avoid-version +module_esound_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-esound.la libsocket-server.la + +module_esound_protocol_tcp6_la_SOURCES = modules/module-protocol-stub.c +module_esound_protocol_tcp6_la_CFLAGS = -DUSE_TCP6_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS) +module_esound_protocol_tcp6_la_LDFLAGS = -module -avoid-version +module_esound_protocol_tcp6_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-esound.la libsocket-server.la + +module_esound_protocol_unix_la_SOURCES = modules/module-protocol-stub.c +module_esound_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS) +module_esound_protocol_unix_la_LDFLAGS = -module -avoid-version +module_esound_protocol_unix_la_LIBADD = $(AM_LIBADD) libpolypcore.la libprotocol-esound.la libsocket-server.la libsocket-util.la + +module_esound_compat_spawnfd_la_SOURCES = modules/module-esound-compat-spawnfd.c +module_esound_compat_spawnfd_la_LDFLAGS = -module -avoid-version +module_esound_compat_spawnfd_la_LIBADD = $(AM_LIBADD) libpolypcore.la + +module_esound_compat_spawnpid_la_SOURCES = modules/module-esound-compat-spawnpid.c +module_esound_compat_spawnpid_la_LDFLAGS = -module -avoid-version +module_esound_compat_spawnpid_la_LIBADD = $(AM_LIBADD) libpolypcore.la + +module_esound_sink_la_SOURCES = modules/module-esound-sink.c +module_esound_sink_la_LDFLAGS = -module -avoid-version +module_esound_sink_la_LIBADD = $(AM_LIBADD) libpolypcore.la libiochannel.la libsocket-client.la libauthkey.la + +# Pipes + +module_pipe_sink_la_SOURCES = modules/module-pipe-sink.c +module_pipe_sink_la_LDFLAGS = -module -avoid-version +module_pipe_sink_la_LIBADD = $(AM_LIBADD) libpolypcore.la libiochannel.la + +module_pipe_source_la_SOURCES = modules/module-pipe-source.c +module_pipe_source_la_LDFLAGS = -module -avoid-version +module_pipe_source_la_LIBADD = $(AM_LIBADD) libpolypcore.la libiochannel.la + +# Fake sources/sinks + +module_sine_la_SOURCES = modules/module-sine.c +module_sine_la_LDFLAGS = -module -avoid-version +module_sine_la_LIBADD = $(AM_LIBADD) libpolypcore.la + +module_null_sink_la_SOURCES = modules/module-null-sink.c +module_null_sink_la_LDFLAGS = -module -avoid-version +module_null_sink_la_LIBADD = $(AM_LIBADD) libpolypcore.la + +# Couplings + +module_combine_la_SOURCES = modules/module-combine.c +module_combine_la_LDFLAGS = -module -avoid-version +module_combine_la_LIBADD = $(AM_LIBADD) libpolypcore.la + +module_match_la_SOURCES = modules/module-match.c +module_match_la_LDFLAGS = -module -avoid-version +module_match_la_LIBADD = $(AM_LIBADD) libpolypcore.la + +module_tunnel_sink_la_SOURCES = modules/module-tunnel.c +module_tunnel_sink_la_CFLAGS = -DTUNNEL_SINK=1 $(AM_CFLAGS) +module_tunnel_sink_la_LDFLAGS = -module -avoid-version +module_tunnel_sink_la_LIBADD = $(AM_LIBADD) libpolypcore.la libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libsocket-util.la libiochannel.la + +module_tunnel_source_la_SOURCES = modules/module-tunnel.c +module_tunnel_source_la_LDFLAGS = -module -avoid-version +module_tunnel_source_la_LIBADD = $(AM_LIBADD) libpolypcore.la libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauthkey-prop.la libsocket-util.la libiochannel.la + +# X11 + +module_x11_bell_la_SOURCES = modules/module-x11-bell.c +module_x11_bell_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS) +module_x11_bell_la_LDFLAGS = -module -avoid-version +module_x11_bell_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la + +module_x11_publish_la_SOURCES = modules/module-x11-publish.c +module_x11_publish_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS) +module_x11_publish_la_LDFLAGS = -module -avoid-version +module_x11_publish_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libauthkey.la libauthkey-prop.la libx11prop.la libstrlist.la + +# OSS + +module_oss_la_SOURCES = modules/module-oss.c +module_oss_la_LDFLAGS = -module -avoid-version +module_oss_la_LIBADD = $(AM_LIBADD) libiochannel.la liboss-util.la + +module_oss_mmap_la_SOURCES = modules/module-oss-mmap.c +module_oss_mmap_la_LDFLAGS = -module -avoid-version +module_oss_mmap_la_LIBADD = $(AM_LIBADD) liboss-util.la + +# ALSA + +module_alsa_sink_la_SOURCES = modules/module-alsa-sink.c +module_alsa_sink_la_LDFLAGS = -module -avoid-version +module_alsa_sink_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la +module_alsa_sink_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) + +module_alsa_source_la_SOURCES = modules/module-alsa-source.c +module_alsa_source_la_LDFLAGS = -module -avoid-version +module_alsa_source_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la +module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) + +# Solaris + +module_solaris_la_SOURCES = modules/module-solaris.c +module_solaris_la_LDFLAGS = -module -avoid-version +module_solaris_la_LIBADD = $(AM_LIBADD) libiochannel.la + +# HOWL + +module_zeroconf_publish_la_SOURCES = modules/module-zeroconf-publish.c +module_zeroconf_publish_la_LDFLAGS = -module -avoid-version +module_zeroconf_publish_la_LIBADD = $(AM_LIBADD) $(HOWL_LIBS) libhowl-wrap.la +module_zeroconf_publish_la_CFLAGS = $(AM_CFLAGS) $(HOWL_CFLAGS) + +# LIRC + +module_lirc_la_SOURCES = modules/module-lirc.c +module_lirc_la_LDFLAGS = -module -avoid-version +module_lirc_la_LIBADD = $(AM_LIBADD) $(LIRC_LIBS) +module_lirc_la_CFLAGS = $(AM_CFLAGS) $(LIRC_CFLAGS) + +# Linux evdev + +module_mmkbd_evdev_la_SOURCES = modules/module-mmkbd-evdev.c +module_mmkbd_evdev_la_LDFLAGS = -module -avoid-version +module_mmkbd_evdev_la_LIBADD = $(AM_LIBADD) +module_mmkbd_evdev_la_CFLAGS = $(AM_CFLAGS) + +# Windows waveout + +module_waveout_la_SOURCES = modules/module-waveout.c +module_waveout_la_LDFLAGS = -module -avoid-version +module_waveout_la_LIBADD = $(AM_LIBADD) libpolypcore.la -lwinmm +module_waveout_la_CFLAGS = $(AM_CFLAGS) + +# Hardware autodetection module +module_detect_la_SOURCES = modules/module-detect.c +module_detect_la_LDFLAGS = -module -avoid-version +module_detect_la_LIBADD = $(AM_LIBADD) +module_detect_la_CFLAGS = $(AM_CFLAGS) + +################################### +# Some minor stuff # +################################### + +suid: polypaudio + chown root $< + chmod u+s $< + +utils/esdcompat.sh: utils/esdcompat.sh.in Makefile + sed -e 's,@PACKAGE_VERSION\@,$(PACKAGE_VERSION),g' \ + -e 's,@PACKAGE_NAME\@,$(PACKAGE_NAME),g' \ + -e 's,@POLYPAUDIO_BINARY\@,$(POLYPAUDIO_BINARY),g' < $< > $@ + +client.conf: client.conf.in Makefile + sed -e 's,@POLYPAUDIO_BINARY\@,$(POLYPAUDIO_BINARY),g' < $< > $@ + +if OS_IS_WIN32 +default.pa: default.pa.win32 + cp $< $@ +else +default.pa: default.pa.in Makefile + sed -e 's,@POLYPAUDIO_BINARY\@,$(POLYPAUDIO_BINARY),g' < $< > $@ +endif + +daemon.conf: daemon.conf.in Makefile + sed -e 's,@DLSEARCHPATH\@,$(modlibdir),g' \ + -e 's,@DEFAULT_CONFIG_FILE\@,$(DEFAULT_CONFIG_DIR),g' < $< > $@ + +install-exec-hook: + chown root $(DESTDIR)$(bindir)/polypaudio ; true + chmod u+s $(DESTDIR)$(bindir)/polypaudio + ln -sf pacat $(DESTDIR)$(bindir)/parec diff --git a/src/client.conf.in b/src/client.conf.in new file mode 100644 index 00000000..fbf645a4 --- /dev/null +++ b/src/client.conf.in @@ -0,0 +1,39 @@ +# $Id$ +# +# This file is part of polypaudio. +# +# polypaudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# polypaudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with polypaudio; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + +## Configuration file for polypaudio clients. Default values are +## commented out. Use either ; or # for commenting + +## Path to the polypaudio daemon to run when autospawning. +; daemon-binary = @POLYPAUDIO_BINARY@ + +## Extra arguments to pass to the polypaudio daemon +; extra-arguments = --log-target=syslog --exit-idle-time=5 + +## The default sink to connect to +; default-sink = + +## The default source to connect to +; default-source = + +## The default sever to connect to +; default-server = + +## Autospawn daemons? +; autospawn = 0 diff --git a/src/daemon.conf.in b/src/daemon.conf.in new file mode 100644 index 00000000..d5373018 --- /dev/null +++ b/src/daemon.conf.in @@ -0,0 +1,77 @@ +# $Id$ +# +# This file is part of polypaudio. +# +# polypaudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# polypaudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with polypaudio; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + +## Configuration file for the polypaudio daemon. Default values are +## commented out. Use either ; or # for commenting + +# Extra verbositiy +; verbose = 0 + +## Daemonize after startup +; daemonize = 0 + +## Quit if startup fails +; fail = 1 + +## Renice the daemon to level -15 and try to get SCHED_FIFO +## scheduling. This a good idea if you hear annyoing noise in the +## playback. However, this is a certain security issue, since it works +## when called SUID root only. root is dropped immediately after gaining +## the nice level and SCHED_FIFO scheduling on startup. +; high-priority = 0 + +## Disallow module loading after startup +; disallow-module-loading = 0 + +## Terminate the daemon after the last client quit and this time +## passed. Use a negative value to disable this feature. +; exit-idle-time = -1 + +## Unload autoloaded modules after being idle for this time +; module-idle-time = 20 + +## The path were to look for dynamic shared objects (DSOs aka +## plugins). You may specify more than one path seperated by +## colons. +; dl-search-path = @DLSEARCHPATH@ + +## The default script file to load. Specify an empty string for not +## loading a default script file. The +; default-script-file = @DEFAULT_CONFIG_FILE@ + +## The default log target. Use either "stderr", "syslog" or +## "auto". The latter is equivalent to "sylog" in case daemonize is +## true, otherwise to "stderr". +; log-target = auto + +## The resampling algorithm to use. Use one of src-sinc-best-quality, +## src-sinc-medium-quality, src-sinc-fastest, src-zero-order-hold, +## src-linear, trivial. See the documentation of libsamplerate for an +## explanation for the different methods. The method 'trivial' is the +## only algorithm implemented without usage of floating point +## numbers. If you're tight on CPU consider using this. On the other +## hand it has the worst quality of all. +; resample-method = sinc-fastest + +## Create a PID file in /tmp/polypaudio-$USER/pid. Of this is enabled +## you may use commands like "polypaudio --kill" or "polypaudio +## --check". If you are planning to start more than one polypaudio +## process per user, you better disable this option since it +## effectively disables multiple instances. +; use-pid-file = 1 diff --git a/src/daemon/caps.c b/src/daemon/caps.c new file mode 100644 index 00000000..8d429459 --- /dev/null +++ b/src/daemon/caps.c @@ -0,0 +1,131 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#ifdef HAVE_SYS_CAPABILITY_H +#include +#endif + +#include +#include "caps.h" + +#ifdef HAVE_GETUID + +/* Drop root rights when called SUID root */ +void pa_drop_root(void) { + uid_t uid = getuid(); + + if (uid == 0 || geteuid() != 0) + return; + + pa_log_info(__FILE__": dropping root rights.\n"); + +#if defined(HAVE_SETRESUID) + setresuid(uid, uid, uid); +#elif defined(HAVE_SETREUID) + setreuid(uid, uid); +#else + setuid(uid); + seteuid(uid); +#endif +} + +#else + +void pa_drop_root(void) { +} + +#endif + +#ifdef HAVE_SYS_CAPABILITY_H + +/* Limit capabilities set to CAPSYS_NICE */ +int pa_limit_caps(void) { + int r = -1; + cap_t caps; + cap_value_t nice_cap = CAP_SYS_NICE; + + caps = cap_init(); + assert(caps); + + cap_clear(caps); + + cap_set_flag(caps, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET); + cap_set_flag(caps, CAP_PERMITTED, 1, &nice_cap, CAP_SET); + + if (cap_set_proc(caps) < 0) + goto fail; + + pa_log_info(__FILE__": dropped capabilities successfully.\n"); + + r = 0; + +fail: + cap_free (caps); + + return r; +} + +/* Drop all capabilities, effectively becoming a normal user */ +int pa_drop_caps(void) { + cap_t caps; + int r = -1; + + caps = cap_init(); + assert(caps); + + cap_clear(caps); + + if (cap_set_proc(caps) < 0) { + pa_log(__FILE__": failed to drop capabilities: %s\n", strerror(errno)); + goto fail; + } + + r = 0; + +fail: + cap_free (caps); + + return r; +} + +#else + +/* NOOPs in case capabilities are not available. */ +int pa_limit_caps(void) { + return 0; +} + +int pa_drop_caps(void) { + pa_drop_root(); + return 0; +} + +#endif + diff --git a/src/daemon/caps.h b/src/daemon/caps.h new file mode 100644 index 00000000..3bb861d1 --- /dev/null +++ b/src/daemon/caps.h @@ -0,0 +1,29 @@ +#ifndef foocapshfoo +#define foocapshfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +void pa_drop_root(void); +int pa_limit_caps(void); +int pa_drop_caps(void); + +#endif diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c new file mode 100644 index 00000000..0b5f9ec7 --- /dev/null +++ b/src/daemon/cmdline.c @@ -0,0 +1,300 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "cmdline.h" +#include +#include +#include + +/* Argument codes for getopt_long() */ +enum { + ARG_HELP = 256, + ARG_VERSION, + ARG_DUMP_CONF, + ARG_DUMP_MODULES, + ARG_DAEMONIZE, + ARG_FAIL, + ARG_LOG_LEVEL, + ARG_HIGH_PRIORITY, + ARG_DISALLOW_MODULE_LOADING, + ARG_EXIT_IDLE_TIME, + ARG_MODULE_IDLE_TIME, + ARG_SCACHE_IDLE_TIME, + ARG_LOG_TARGET, + ARG_LOAD, + ARG_FILE, + ARG_DL_SEARCH_PATH, + ARG_RESAMPLE_METHOD, + ARG_KILL, + ARG_USE_PID_FILE, + ARG_CHECK +}; + +/* Tabel for getopt_long() */ +static struct option long_options[] = { + {"help", 0, 0, ARG_HELP}, + {"version", 0, 0, ARG_VERSION}, + {"dump-conf", 0, 0, ARG_DUMP_CONF}, + {"dump-modules", 0, 0, ARG_DUMP_MODULES}, + {"daemonize", 2, 0, ARG_DAEMONIZE}, + {"fail", 2, 0, ARG_FAIL}, + {"verbose", 2, 0, ARG_LOG_LEVEL}, + {"log-level", 2, 0, ARG_LOG_LEVEL}, + {"high-priority", 2, 0, ARG_HIGH_PRIORITY}, + {"disallow-module-loading", 2, 0, ARG_DISALLOW_MODULE_LOADING}, + {"exit-idle-time", 2, 0, ARG_EXIT_IDLE_TIME}, + {"module-idle-time", 2, 0, ARG_MODULE_IDLE_TIME}, + {"scache-idle-time", 2, 0, ARG_SCACHE_IDLE_TIME}, + {"log-target", 1, 0, ARG_LOG_TARGET}, + {"load", 1, 0, ARG_LOAD}, + {"file", 1, 0, ARG_FILE}, + {"dl-search-path", 1, 0, ARG_DL_SEARCH_PATH}, + {"resample-method", 1, 0, ARG_RESAMPLE_METHOD}, + {"kill", 0, 0, ARG_KILL}, + {"use-pid-file", 2, 0, ARG_USE_PID_FILE}, + {"check", 0, 0, ARG_CHECK}, + {NULL, 0, 0, 0} +}; + +void pa_cmdline_help(const char *argv0) { + const char *e; + + if ((e = strrchr(argv0, '/'))) + e++; + else + e = argv0; + + printf("%s [options]\n\n" + "COMMANDS:\n" + " -h, --help Show this help\n" + " --version Show version\n" + " --dump-conf Dump default configuration\n" + " --dump-modules Dump list of available modules\n" + " -k --kill Kill a running daemon\n" + " --check Check for a running daemon\n\n" + + "OPTIONS:\n" + " -D, --daemonize[=BOOL] Daemonize after startup\n" + " --fail[=BOOL] Quit when startup fails\n" + " --high-priority[=BOOL] Try to set high process priority\n" + " (only available as root)\n" + " --disallow-module-loading[=BOOL] Disallow module loading after startup\n" + " --exit-idle-time=SECS Terminate the daemon when idle and this\n" + " time passed\n" + " --module-idle-time=SECS Unload autoloaded modules when idle and\n" + " this time passed\n" + " --scache-idle-time=SECS Unload autoloaded samples when idle and\n" + " this time passed\n" + " --log-level[=LEVEL] Increase or set verbosity level\n" + " -v Increase the verbosity level\n" + " --log-target={auto,syslog,stderr} Specify the log target\n" + " -p, --dl-search-path=PATH Set the search path for dynamic shared\n" + " objects (plugins)\n" + " --resample-method=[METHOD] Use the specified resampling method\n" + " (one of src-sinc-medium-quality,\n" + " src-sinc-best-quality,src-sinc-fastest\n" + " src-zero-order-hold,src-linear,trivial)\n" + " --use-pid-file[=BOOL] Create a PID file\n\n" + + "STARTUP SCRIPT:\n" + " -L, --load=\"MODULE ARGUMENTS\" Load the specified plugin module with\n" + " the specified argument\n" + " -F, --file=FILENAME Run the specified script\n" + " -C Open a command line on the running TTY\n" + " after startup\n\n" + + " -n Don't load default script file\n", e); +} + +int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d) { + pa_strbuf *buf = NULL; + int c; + assert(conf && argc && argv); + + buf = pa_strbuf_new(); + + if (conf->script_commands) + pa_strbuf_puts(buf, conf->script_commands); + + while ((c = getopt_long(argc, argv, "L:F:ChDnp:kv", long_options, NULL)) != -1) { + switch (c) { + case ARG_HELP: + case 'h': + conf->cmd = PA_CMD_HELP; + break; + + case ARG_VERSION: + conf->cmd = PA_CMD_VERSION; + break; + + case ARG_DUMP_CONF: + conf->cmd = PA_CMD_DUMP_CONF; + break; + + case ARG_DUMP_MODULES: + conf->cmd = PA_CMD_DUMP_MODULES; + break; + + case 'k': + case ARG_KILL: + conf->cmd = PA_CMD_KILL; + break; + + case ARG_CHECK: + conf->cmd = PA_CMD_CHECK; + break; + + case ARG_LOAD: + case 'L': + pa_strbuf_printf(buf, "load-module %s\n", optarg); + break; + + case ARG_FILE: + case 'F': + pa_strbuf_printf(buf, ".include %s\n", optarg); + break; + + case 'C': + pa_strbuf_puts(buf, "load-module module-cli\n"); + break; + + case ARG_DAEMONIZE: + case 'D': + if ((conf->daemonize = optarg ? pa_parse_boolean(optarg) : 1) < 0) { + pa_log(__FILE__": --daemonize expects boolean argument\n"); + goto fail; + } + break; + + case ARG_FAIL: + if ((conf->fail = optarg ? pa_parse_boolean(optarg) : 1) < 0) { + pa_log(__FILE__": --fail expects boolean argument\n"); + goto fail; + } + break; + + case 'v': + case ARG_LOG_LEVEL: + + if (optarg) { + if (pa_daemon_conf_set_log_level(conf, optarg) < 0) { + pa_log(__FILE__": --log-level expects log level argument (either numeric in range 0..4 or one of debug, info, notice, warn, error).\n"); + goto fail; + } + } else { + if (conf->log_level < PA_LOG_LEVEL_MAX-1) + conf->log_level++; + } + + break; + + case ARG_HIGH_PRIORITY: + if ((conf->high_priority = optarg ? pa_parse_boolean(optarg) : 1) < 0) { + pa_log(__FILE__": --high-priority expects boolean argument\n"); + goto fail; + } + break; + + case ARG_DISALLOW_MODULE_LOADING: + if ((conf->disallow_module_loading = optarg ? pa_parse_boolean(optarg) : 1) < 0) { + pa_log(__FILE__": --disallow-module-loading expects boolean argument\n"); + goto fail; + } + break; + + case ARG_USE_PID_FILE: + if ((conf->use_pid_file = optarg ? pa_parse_boolean(optarg) : 1) < 0) { + pa_log(__FILE__": --use-pid-file expects boolean argument\n"); + goto fail; + } + break; + + case 'p': + case ARG_DL_SEARCH_PATH: + pa_xfree(conf->dl_search_path); + conf->dl_search_path = *optarg ? pa_xstrdup(optarg) : NULL; + break; + + case 'n': + pa_xfree(conf->default_script_file); + conf->default_script_file = NULL; + break; + + case ARG_LOG_TARGET: + if (pa_daemon_conf_set_log_target(conf, optarg) < 0) { + pa_log(__FILE__": Invalid log target: use either 'syslog', 'stderr' or 'auto'.\n"); + goto fail; + } + break; + + case ARG_EXIT_IDLE_TIME: + conf->exit_idle_time = atoi(optarg); + break; + + case ARG_MODULE_IDLE_TIME: + conf->module_idle_time = atoi(optarg); + break; + + case ARG_SCACHE_IDLE_TIME: + conf->scache_idle_time = atoi(optarg); + break; + + case ARG_RESAMPLE_METHOD: + if (pa_daemon_conf_set_resample_method(conf, optarg) < 0) { + pa_log(__FILE__": Invalid resample method '%s'.\n", optarg); + goto fail; + } + break; + + default: + goto fail; + } + } + + pa_xfree(conf->script_commands); + conf->script_commands = pa_strbuf_tostring_free(buf); + + if (!conf->script_commands) { + pa_xfree(conf->script_commands); + conf->script_commands = NULL; + } + + *d = optind; + + return 0; + +fail: + if (buf) + pa_strbuf_free(buf); + + return -1; +} diff --git a/src/daemon/cmdline.h b/src/daemon/cmdline.h new file mode 100644 index 00000000..e2eaf0d2 --- /dev/null +++ b/src/daemon/cmdline.h @@ -0,0 +1,35 @@ +#ifndef foocmdlinehfoo +#define foocmdlinehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "daemon-conf.h" + +/* Parese the command line and store its data in *c. Return the index + * of the first unparsed argument in *d. */ +int pa_cmdline_parse(pa_daemon_conf*c, int argc, char *const argv [], int *d); + +/* Show the command line help. The command name is extracted from + * argv[0] which should be passed in argv0. */ +void pa_cmdline_help(const char *argv0); + +#endif diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c new file mode 100644 index 00000000..6887796f --- /dev/null +++ b/src/daemon/cpulimit.c @@ -0,0 +1,236 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "cpulimit.h" +#include +#include + +#ifdef HAVE_SIGXCPU + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif + +/* This module implements a watchdog that makes sure that the current + * process doesn't consume more than 70% CPU time for 10 seconds. This + * is very useful when using SCHED_FIFO scheduling which effectively + * disables multitasking. */ + +/* Method of operation: Using SIGXCPU a signal handler is called every + * 10s process CPU time. That function checks if less than 14s system + * time have passed. In that case, it tries to contact the main event + * loop through a pipe. After two additional seconds it is checked + * whether the main event loop contact was successful. If not, the + * program is terminated forcibly. */ + +/* Utilize this much CPU time at maximum */ +#define CPUTIME_PERCENT 70 + +/* Check every 10s */ +#define CPUTIME_INTERVAL_SOFT (10) + +/* Recheck after 2s */ +#define CPUTIME_INTERVAL_HARD (2) + +/* Time of the last CPU load check */ +static time_t last_time = 0; + +/* Pipe for communicating with the main loop */ +static int the_pipe[2] = {-1, -1}; + +/* Main event loop and IO event for the FIFO */ +static pa_mainloop_api *api = NULL; +static pa_io_event *io_event = NULL; + +/* Saved sigaction struct for SIGXCPU */ +static struct sigaction sigaction_prev; + +/* Nonzero after pa_cpu_limit_init() */ +static int installed = 0; + +/* The current state of operation */ +static enum { + PHASE_IDLE, /* Normal state */ + PHASE_SOFT /* After CPU overload has been detected */ +} phase = PHASE_IDLE; + +/* Reset the SIGXCPU timer to the next t seconds */ +static void reset_cpu_time(int t) { + int r; + long n; + struct rlimit rl; + struct rusage ru; + + /* Get the current CPU time of the current process */ + r = getrusage(RUSAGE_SELF, &ru); + assert(r >= 0); + + n = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec + t; + + r = getrlimit(RLIMIT_CPU, &rl); + assert(r >= 0); + + rl.rlim_cur = n; + r = setrlimit(RLIMIT_CPU, &rl); + assert(r >= 0); +} + +/* A simple, thread-safe puts() work-alike */ +static void write_err(const char *p) { + pa_loop_write(2, p, strlen(p)); +} + +/* The signal handler, called on every SIGXCPU */ +static void signal_handler(int sig) { + assert(sig == SIGXCPU); + + if (phase == PHASE_IDLE) { + time_t now; + +#ifdef PRINT_CPU_LOAD + char t[256]; +#endif + + time(&now); + +#ifdef PRINT_CPU_LOAD + snprintf(t, sizeof(t), "Using %0.1f%% CPU\n", (double)CPUTIME_INTERVAL_SOFT/(now-last_time)*100); + write_err(t); +#endif + + if (CPUTIME_INTERVAL_SOFT >= ((now-last_time)*(double)CPUTIME_PERCENT/100)) { + static const char c = 'X'; + + write_err("Soft CPU time limit exhausted, terminating.\n"); + + /* Try a soft cleanup */ + write(the_pipe[1], &c, sizeof(c)); + phase = PHASE_SOFT; + reset_cpu_time(CPUTIME_INTERVAL_HARD); + + } else { + + /* Everything's fine */ + reset_cpu_time(CPUTIME_INTERVAL_SOFT); + last_time = now; + } + + } else if (phase == PHASE_SOFT) { + write_err("Hard CPU time limit exhausted, terminating forcibly.\n"); + _exit(1); /* Forced exit */ + } +} + +/* Callback for IO events on the FIFO */ +static void callback(pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags f, void *userdata) { + char c; + assert(m && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == the_pipe[0]); + read(the_pipe[0], &c, sizeof(c)); + m->quit(m, 1); /* Quit the main loop */ +} + +/* Initializes CPU load limiter */ +int pa_cpu_limit_init(pa_mainloop_api *m) { + struct sigaction sa; + assert(m && !api && !io_event && the_pipe[0] == -1 && the_pipe[1] == -1 && !installed); + + time(&last_time); + + /* Prepare the main loop pipe */ + if (pipe(the_pipe) < 0) { + pa_log(__FILE__": pipe() failed: %s\n", strerror(errno)); + return -1; + } + + pa_make_nonblock_fd(the_pipe[0]); + pa_make_nonblock_fd(the_pipe[1]); + pa_fd_set_cloexec(the_pipe[0], 1); + pa_fd_set_cloexec(the_pipe[1], 1); + + api = m; + io_event = api->io_new(m, the_pipe[0], PA_IO_EVENT_INPUT, callback, NULL); + + phase = PHASE_IDLE; + + /* Install signal handler for SIGXCPU */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = signal_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if (sigaction(SIGXCPU, &sa, &sigaction_prev) < 0) { + pa_cpu_limit_done(); + return -1; + } + + installed = 1; + + reset_cpu_time(CPUTIME_INTERVAL_SOFT); + + return 0; +} + +/* Shutdown CPU load limiter */ +void pa_cpu_limit_done(void) { + int r; + + if (io_event) { + assert(api); + api->io_free(io_event); + io_event = NULL; + api = NULL; + } + + if (the_pipe[0] >= 0) + close(the_pipe[0]); + if (the_pipe[1] >= 0) + close(the_pipe[1]); + the_pipe[0] = the_pipe[1] = -1; + + if (installed) { + r = sigaction(SIGXCPU, &sigaction_prev, NULL); + assert(r >= 0); + installed = 0; + } +} + +#else /* HAVE_SIGXCPU */ + +int pa_cpu_limit_init(PA_GCC_UNUSED pa_mainloop_api *m) { + return 0; +} + +void pa_cpu_limit_done(void) { +} + +#endif diff --git a/src/daemon/cpulimit.h b/src/daemon/cpulimit.h new file mode 100644 index 00000000..f3c5534d --- /dev/null +++ b/src/daemon/cpulimit.h @@ -0,0 +1,34 @@ +#ifndef foocpulimithfoo +#define foocpulimithfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +/* This kills the polypaudio process if it eats more than 70% of the + * CPU time. This is build around setrlimit() and SIGXCPU. It is handy + * in case of using SCHED_FIFO which may freeze the whole machine */ + +int pa_cpu_limit_init(pa_mainloop_api *m); +void pa_cpu_limit_done(void); + +#endif diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c new file mode 100644 index 00000000..8fe3c4cc --- /dev/null +++ b/src/daemon/daemon-conf.c @@ -0,0 +1,297 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "daemon-conf.h" +#include +#include +#include +#include +#include + +#ifndef DEFAULT_CONFIG_DIR +# ifndef OS_IS_WIN32 +# define DEFAULT_CONFIG_DIR "/etc/polypaudio" +# else +# define DEFAULT_CONFIG_DIR "%POLYP_ROOT%" +# endif +#endif + +#ifndef OS_IS_WIN32 +# define PATH_SEP "/" +#else +# define PATH_SEP "\\" +#endif + +#define DEFAULT_SCRIPT_FILE DEFAULT_CONFIG_DIR PATH_SEP "default.pa" +#define DEFAULT_SCRIPT_FILE_USER ".polypaudio" PATH_SEP "default.pa" +#define DEFAULT_CONFIG_FILE DEFAULT_CONFIG_DIR PATH_SEP "daemon.conf" +#define DEFAULT_CONFIG_FILE_USER ".polypaudio" PATH_SEP "daemon.conf" + +#define ENV_SCRIPT_FILE "POLYP_SCRIPT" +#define ENV_CONFIG_FILE "POLYP_CONFIG" +#define ENV_DL_SEARCH_PATH "POLYP_DLPATH" + +static const pa_daemon_conf default_conf = { + .cmd = PA_CMD_DAEMON, + .daemonize = 0, + .fail = 1, + .high_priority = 0, + .disallow_module_loading = 0, + .exit_idle_time = -1, + .module_idle_time = 20, + .scache_idle_time = 20, + .auto_log_target = 1, + .script_commands = NULL, + .dl_search_path = NULL, + .default_script_file = NULL, + .log_target = PA_LOG_SYSLOG, + .log_level = PA_LOG_NOTICE, + .resample_method = PA_RESAMPLER_SRC_SINC_FASTEST, + .config_file = NULL, + .use_pid_file = 1 +}; + +pa_daemon_conf* pa_daemon_conf_new(void) { + FILE *f; + pa_daemon_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf)); + + if ((f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file))) + fclose(f); + +#ifdef DLSEARCHPATH + c->dl_search_path = pa_xstrdup(DLSEARCHPATH); +#endif + return c; +} + +void pa_daemon_conf_free(pa_daemon_conf *c) { + assert(c); + pa_xfree(c->script_commands); + pa_xfree(c->dl_search_path); + pa_xfree(c->default_script_file); + pa_xfree(c->config_file); + pa_xfree(c); +} + +int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string) { + assert(c && string); + + if (!strcmp(string, "auto")) + c->auto_log_target = 1; + else if (!strcmp(string, "syslog")) { + c->auto_log_target = 0; + c->log_target = PA_LOG_SYSLOG; + } else if (!strcmp(string, "stderr")) { + c->auto_log_target = 0; + c->log_target = PA_LOG_STDERR; + } else + return -1; + + return 0; +} + +int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string) { + uint32_t u; + assert(c && string); + + if (pa_atou(string, &u) >= 0) { + if (u >= PA_LOG_LEVEL_MAX) + return -1; + + c->log_level = (pa_log_level_t) u; + } else if (pa_startswith(string, "debug")) + c->log_level = PA_LOG_DEBUG; + else if (pa_startswith(string, "info")) + c->log_level = PA_LOG_INFO; + else if (pa_startswith(string, "notice")) + c->log_level = PA_LOG_NOTICE; + else if (pa_startswith(string, "warn")) + c->log_level = PA_LOG_WARN; + else if (pa_startswith(string, "err")) + c->log_level = PA_LOG_ERROR; + else + return -1; + + return 0; +} + +int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) { + int m; + assert(c && string); + + if ((m = pa_parse_resample_method(string)) < 0) + return -1; + + c->resample_method = m; + return 0; +} + +static int parse_log_target(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { + pa_daemon_conf *c = data; + assert(filename && lvalue && rvalue && data); + + if (pa_daemon_conf_set_log_target(c, rvalue) < 0) { + pa_log(__FILE__": [%s:%u] Invalid log target '%s'.\n", filename, line, rvalue); + return -1; + } + + return 0; +} + +static int parse_log_level(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { + pa_daemon_conf *c = data; + assert(filename && lvalue && rvalue && data); + + if (pa_daemon_conf_set_log_level(c, rvalue) < 0) { + pa_log(__FILE__": [%s:%u] Invalid log level '%s'.\n", filename, line, rvalue); + return -1; + } + + return 0; +} + +static int parse_resample_method(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { + pa_daemon_conf *c = data; + assert(filename && lvalue && rvalue && data); + + if (pa_daemon_conf_set_resample_method(c, rvalue) < 0) { + pa_log(__FILE__": [%s:%u] Inavalid resample method '%s'.\n", filename, line, rvalue); + return -1; + } + + return 0; +} + +int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { + int r = -1; + FILE *f = NULL; + + pa_config_item table[] = { + { "daemonize", pa_config_parse_bool, NULL }, + { "fail", pa_config_parse_bool, NULL }, + { "high-priority", pa_config_parse_bool, NULL }, + { "disallow-module-loading", pa_config_parse_bool, NULL }, + { "exit-idle-time", pa_config_parse_int, NULL }, + { "module-idle-time", pa_config_parse_int, NULL }, + { "scache-idle-time", pa_config_parse_int, NULL }, + { "dl-search-path", pa_config_parse_string, NULL }, + { "default-script-file", pa_config_parse_string, NULL }, + { "log-target", parse_log_target, NULL }, + { "log-level", parse_log_level, NULL }, + { "verbose", parse_log_level, NULL }, + { "resample-method", parse_resample_method, NULL }, + { "use-pid-file", pa_config_parse_bool, NULL }, + { NULL, NULL, NULL }, + }; + + table[0].data = &c->daemonize; + table[1].data = &c->fail; + table[2].data = &c->high_priority; + table[3].data = &c->disallow_module_loading; + table[4].data = &c->exit_idle_time; + table[5].data = &c->module_idle_time; + table[6].data = &c->scache_idle_time; + table[7].data = &c->dl_search_path; + table[8].data = &c->default_script_file; + table[9].data = c; + table[10].data = c; + table[11].data = c; + table[12].data = c; + table[13].data = &c->use_pid_file; + + pa_xfree(c->config_file); + c->config_file = NULL; + + f = filename ? + fopen(c->config_file = pa_xstrdup(filename), "r") : + pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file); + + if (!f && errno != ENOENT) { + pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno)); + goto finish; + } + + r = f ? pa_config_parse(c->config_file, f, table, NULL) : 0; + +finish: + if (f) + fclose(f); + + return r; +} + +int pa_daemon_conf_env(pa_daemon_conf *c) { + char *e; + + if ((e = getenv(ENV_DL_SEARCH_PATH))) { + pa_xfree(c->dl_search_path); + c->dl_search_path = pa_xstrdup(e); + } + if ((e = getenv(ENV_SCRIPT_FILE))) { + pa_xfree(c->default_script_file); + c->default_script_file = pa_xstrdup(e); + } + + return 0; +} + +static const char* const log_level_to_string[] = { + [PA_LOG_DEBUG] = "debug", + [PA_LOG_INFO] = "info", + [PA_LOG_NOTICE] = "notice", + [PA_LOG_WARN] = "warning", + [PA_LOG_ERROR] = "error" +}; + +char *pa_daemon_conf_dump(pa_daemon_conf *c) { + pa_strbuf *s = pa_strbuf_new(); + + if (c->config_file) + pa_strbuf_printf(s, "### Read from configuration file: %s ###\n", c->config_file); + + assert(c->log_level <= PA_LOG_LEVEL_MAX); + + pa_strbuf_printf(s, "daemonize = %i\n", !!c->daemonize); + pa_strbuf_printf(s, "fail = %i\n", !!c->fail); + pa_strbuf_printf(s, "high-priority = %i\n", !!c->high_priority); + pa_strbuf_printf(s, "disallow-module-loading = %i\n", !!c->disallow_module_loading); + pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time); + pa_strbuf_printf(s, "module-idle-time = %i\n", c->module_idle_time); + pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time); + pa_strbuf_printf(s, "dl-search-path = %s\n", c->dl_search_path ? c->dl_search_path : ""); + pa_strbuf_printf(s, "default-script-file = %s\n", c->default_script_file); + pa_strbuf_printf(s, "log-target = %s\n", c->auto_log_target ? "auto" : (c->log_target == PA_LOG_SYSLOG ? "syslog" : "stderr")); + pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]); + pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method)); + pa_strbuf_printf(s, "use-pid-file = %i\n", c->use_pid_file); + + return pa_strbuf_tostring_free(s); +} diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h new file mode 100644 index 00000000..d5131419 --- /dev/null +++ b/src/daemon/daemon-conf.h @@ -0,0 +1,80 @@ +#ifndef foodaemonconfhfoo +#define foodaemonconfhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +/* The actual command to execute */ +typedef enum pa_daemon_conf_cmd { + PA_CMD_DAEMON, /* the default */ + PA_CMD_HELP, + PA_CMD_VERSION, + PA_CMD_DUMP_CONF, + PA_CMD_DUMP_MODULES, + PA_CMD_KILL, + PA_CMD_CHECK +} pa_daemon_conf_cmd_t; + +/* A structure containing configuration data for the Polypaudio server . */ +typedef struct pa_daemon_conf { + pa_daemon_conf_cmd_t cmd; + int daemonize, + fail, + high_priority, + disallow_module_loading, + exit_idle_time, + module_idle_time, + scache_idle_time, + auto_log_target, + use_pid_file; + char *script_commands, *dl_search_path, *default_script_file; + pa_log_target_t log_target; + pa_log_level_t log_level; + int resample_method; + char *config_file; +} pa_daemon_conf; + +/* Allocate a new structure and fill it with sane defaults */ +pa_daemon_conf* pa_daemon_conf_new(void); +void pa_daemon_conf_free(pa_daemon_conf*c); + +/* Load configuration data from the specified file overwriting the + * current settings in *c. If filename is NULL load the default daemon + * configuration file */ +int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename); + +/* Pretty print the current configuration data of the daemon. The + * returned string has to be freed manually. The output of this + * function may be parsed with pa_daemon_conf_load(). */ +char *pa_daemon_conf_dump(pa_daemon_conf *c); + +/* Load the configuration data from the process' environment + * overwriting the current settings in *c. */ +int pa_daemon_conf_env(pa_daemon_conf *c); + +/* Set these configuration variables in the structure by passing a string */ +int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string); +int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string); +int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string); + +#endif diff --git a/src/daemon/dumpmodules.c b/src/daemon/dumpmodules.c new file mode 100644 index 00000000..8d8eb0b9 --- /dev/null +++ b/src/daemon/dumpmodules.c @@ -0,0 +1,99 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "dumpmodules.h" +#include +#include + +#define PREFIX "module-" + +static void short_info(const char *name, PA_GCC_UNUSED const char *path, pa_modinfo *i) { + assert(name && i); + printf("%-40s%s\n", name, i->description ? i->description : "n/a"); +} + +static void long_info(const char *name, const char *path, pa_modinfo *i) { + static int nl = 0; + assert(name && i); + + if (nl) + printf("\n"); + + nl = 1; + + printf("Name: %s\n", name); + + if (!i->description && !i->version && !i->author && !i->usage) + printf("No module information available\n"); + else { + if (i->version) + printf("Version: %s\n", i->version); + if (i->description) + printf("Description: %s\n", i->description); + if (i->author) + printf("Author: %s\n", i->author); + if (i->usage) + printf("Usage: %s\n", i->usage); + } + + if (path) + printf("Path: %s\n", path); +} + +static void show_info(const char *name, const char *path, void (*info)(const char *name, const char *path, pa_modinfo*i)) { + pa_modinfo *i; + + if ((i = pa_modinfo_get_by_name(path ? path : name))) { + info(name, path, i); + pa_modinfo_free(i); + } +} + +static int callback(const char *path, lt_ptr data) { + const char *e; + pa_daemon_conf *c = (data); + + e = pa_path_get_filename(path); + + if (strlen(e) > sizeof(PREFIX)-1 && !strncmp(e, PREFIX, sizeof(PREFIX)-1)) + show_info(e, path, c->log_level >= PA_LOG_INFO ? long_info : short_info); + + return 0; +} + +void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]) { + if (argc > 0) { + int i; + for (i = 0; i < argc; i++) + show_info(argv[i], NULL, long_info); + } else + lt_dlforeachfile(NULL, callback, c); +} diff --git a/src/daemon/dumpmodules.h b/src/daemon/dumpmodules.h new file mode 100644 index 00000000..968d2de9 --- /dev/null +++ b/src/daemon/dumpmodules.h @@ -0,0 +1,31 @@ +#ifndef foodumpmoduleshfoo +#define foodumpmoduleshfoo + +/* $Id*/ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "daemon-conf.h" + +/* Dump all available modules to STDOUT. If argc > 0 print information + * about the modules specified in argv[] instead. */ +void pa_dump_modules(pa_daemon_conf *c, int argc, char * const argv[]); + +#endif diff --git a/src/daemon/main.c b/src/daemon/main.c new file mode 100644 index 00000000..6be83d8c --- /dev/null +++ b/src/daemon/main.c @@ -0,0 +1,472 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_LIBWRAP +#include +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include "cmdline.h" +#include +#include +#include +#include +#include "cpulimit.h" +#include +#include "daemon-conf.h" +#include "dumpmodules.h" +#include "caps.h" +#include +#include +#include + +#ifdef HAVE_LIBWRAP +/* Only one instance of these variables */ +int allow_severity = LOG_INFO; +int deny_severity = LOG_WARNING; +#endif + +#ifdef OS_IS_WIN32 + +static void message_cb(pa_mainloop_api*a, pa_defer_event *e, void *userdata) { + MSG msg; + + while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) + raise(SIGTERM); + else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + +#endif + +static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e, int sig, void *userdata) { + pa_log_info(__FILE__": Got signal %s.\n", pa_strsignal(sig)); + + switch (sig) { +#ifdef SIGUSR1 + case SIGUSR1: + pa_module_load(userdata, "module-cli", NULL); + break; +#endif + +#ifdef SIGUSR2 + case SIGUSR2: + pa_module_load(userdata, "module-cli-protocol-unix", NULL); + break; +#endif + +#ifdef SIGHUP + case SIGHUP: { + char *c = pa_full_status_string(userdata); + pa_log_notice(c); + pa_xfree(c); + return; + } +#endif + + case SIGINT: + case SIGTERM: + default: + pa_log_info(__FILE__": Exiting.\n"); + m->quit(m, 1); + break; + } +} + +static void close_pipe(int p[2]) { + if (p[0] != -1) + close(p[0]); + if (p[1] != -1) + close(p[1]); + p[0] = p[1] = -1; +} + +int main(int argc, char *argv[]) { + pa_core *c; + pa_strbuf *buf = NULL; + pa_daemon_conf *conf; + pa_mainloop *mainloop; + + char *s; + int r, retval = 1, d = 0; + int daemon_pipe[2] = { -1, -1 }; + int suid_root; + int valid_pid_file = 0; + +#ifdef HAVE_GETUID + gid_t gid = (gid_t) -1; +#endif + +#ifdef OS_IS_WIN32 + pa_defer_event *defer; +#endif + + pa_limit_caps(); + +#ifdef HAVE_GETUID + suid_root = getuid() != 0 && geteuid() == 0; + + if (suid_root && (pa_uid_in_group("realtime", &gid) <= 0 || gid >= 1000)) { + pa_log_warn(__FILE__": WARNING: called SUID root, but not in group 'realtime'.\n"); + pa_drop_root(); + } +#else + suid_root = 0; +#endif + + LTDL_SET_PRELOADED_SYMBOLS(); + + r = lt_dlinit(); + assert(r == 0); + +#ifdef OS_IS_WIN32 + { + WSADATA data; + WSAStartup(MAKEWORD(2, 0), &data); + } +#endif + + pa_log_set_ident("polypaudio"); + + conf = pa_daemon_conf_new(); + + if (pa_daemon_conf_load(conf, NULL) < 0) + goto finish; + + if (pa_daemon_conf_env(conf) < 0) + goto finish; + + if (pa_cmdline_parse(conf, argc, argv, &d) < 0) { + pa_log(__FILE__": failed to parse command line.\n"); + goto finish; + } + + pa_log_set_maximal_level(conf->log_level); + pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL); + + if (conf->high_priority && conf->cmd == PA_CMD_DAEMON) + pa_raise_priority(); + + pa_drop_caps(); + + if (suid_root) + pa_drop_root(); + + if (conf->dl_search_path) + lt_dlsetsearchpath(conf->dl_search_path); + + switch (conf->cmd) { + case PA_CMD_DUMP_MODULES: + pa_dump_modules(conf, argc-d, argv+d); + retval = 0; + goto finish; + + case PA_CMD_DUMP_CONF: { + s = pa_daemon_conf_dump(conf); + fputs(s, stdout); + pa_xfree(s); + retval = 0; + goto finish; + } + + case PA_CMD_HELP : + pa_cmdline_help(argv[0]); + retval = 0; + goto finish; + + case PA_CMD_VERSION : + printf(PACKAGE_NAME" "PACKAGE_VERSION"\n"); + retval = 0; + goto finish; + + case PA_CMD_CHECK: { + pid_t pid; + + if (pa_pid_file_check_running(&pid) < 0) { + pa_log_info(__FILE__": daemon not running\n"); + } else { + pa_log_info(__FILE__": daemon running as PID %u\n", pid); + retval = 0; + } + + goto finish; + + } + case PA_CMD_KILL: + + if (pa_pid_file_kill(SIGINT, NULL) < 0) + pa_log(__FILE__": failed to kill daemon.\n"); + else + retval = 0; + + goto finish; + + default: + assert(conf->cmd == PA_CMD_DAEMON); + } + + if (conf->daemonize) { + pid_t child; + int tty_fd; + + if (pa_stdio_acquire() < 0) { + pa_log(__FILE__": failed to acquire stdio.\n"); + goto finish; + } + +#ifdef HAVE_FORK + if (pipe(daemon_pipe) < 0) { + pa_log(__FILE__": failed to create pipe.\n"); + goto finish; + } + + if ((child = fork()) < 0) { + pa_log(__FILE__": fork() failed: %s\n", strerror(errno)); + goto finish; + } + + if (child != 0) { + /* Father */ + + close(daemon_pipe[1]); + daemon_pipe[1] = -1; + + if (pa_loop_read(daemon_pipe[0], &retval, sizeof(retval)) != sizeof(retval)) { + pa_log(__FILE__": read() failed: %s\n", strerror(errno)); + retval = 1; + } + + if (retval) + pa_log(__FILE__": daemon startup failed.\n"); + else + pa_log_info(__FILE__": daemon startup successful.\n"); + + goto finish; + } + + close(daemon_pipe[0]); + daemon_pipe[0] = -1; +#endif + + if (conf->auto_log_target) + pa_log_set_target(PA_LOG_SYSLOG, NULL); + +#ifdef HAVE_SETSID + setsid(); +#endif +#ifdef HAVE_SETPGID + setpgid(0,0); +#endif + +#ifndef OS_IS_WIN32 + close(0); + close(1); + close(2); + + open("/dev/null", O_RDONLY); + open("/dev/null", O_WRONLY); + open("/dev/null", O_WRONLY); +#else + FreeConsole(); +#endif + +#ifdef SIGTTOU + signal(SIGTTOU, SIG_IGN); +#endif +#ifdef SIGTTIN + signal(SIGTTIN, SIG_IGN); +#endif +#ifdef SIGTSTP + signal(SIGTSTP, SIG_IGN); +#endif + +#ifdef TIOCNOTTY + if ((tty_fd = open("/dev/tty", O_RDWR)) >= 0) { + ioctl(tty_fd, TIOCNOTTY, (char*) 0); + close(tty_fd); + } +#endif + } + + chdir("/"); + + if (conf->use_pid_file) { + if (pa_pid_file_create() < 0) { + pa_log(__FILE__": pa_pid_file_create() failed.\n"); +#ifdef HAVE_FORK + if (conf->daemonize) + pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); +#endif + goto finish; + } + + valid_pid_file = 1; + } + + mainloop = pa_mainloop_new(); + assert(mainloop); + + c = pa_core_new(pa_mainloop_get_api(mainloop)); + assert(c); + + r = pa_signal_init(pa_mainloop_get_api(mainloop)); + assert(r == 0); + pa_signal_new(SIGINT, signal_callback, c); + pa_signal_new(SIGTERM, signal_callback, c); +#ifdef SIGPIPE + signal(SIGPIPE, SIG_IGN); +#endif + +#ifdef OS_IS_WIN32 + defer = pa_mainloop_get_api(mainloop)->defer_new(pa_mainloop_get_api(mainloop), message_cb, NULL); + assert(defer); +#endif + + if (conf->daemonize) + c->running_as_daemon = 1; + +#ifdef SIGUSR1 + pa_signal_new(SIGUSR1, signal_callback, c); +#endif +#ifdef SIGUSR2 + pa_signal_new(SIGUSR2, signal_callback, c); +#endif +#ifdef SIGHUP + pa_signal_new(SIGHUP, signal_callback, c); +#endif + + oil_init(); + + r = pa_cpu_limit_init(pa_mainloop_get_api(mainloop)); + assert(r == 0); + + buf = pa_strbuf_new(); + assert(buf); + if (conf->default_script_file) + r = pa_cli_command_execute_file(c, conf->default_script_file, buf, &conf->fail); + + if (r >= 0) + r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail); + pa_log(s = pa_strbuf_tostring_free(buf)); + pa_xfree(s); + + if (r < 0 && conf->fail) { + pa_log(__FILE__": failed to initialize daemon.\n"); +#ifdef HAVE_FORK + if (conf->daemonize) + pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); +#endif + } else if (!c->modules || pa_idxset_size(c->modules) == 0) { + pa_log(__FILE__": daemon startup without any loaded modules, refusing to work.\n"); +#ifdef HAVE_FORK + if (conf->daemonize) + pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); +#endif + } else { + + retval = 0; +#ifdef HAVE_FORK + if (conf->daemonize) + pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); +#endif + + c->disallow_module_loading = conf->disallow_module_loading; + c->exit_idle_time = conf->exit_idle_time; + c->module_idle_time = conf->module_idle_time; + c->scache_idle_time = conf->scache_idle_time; + c->resample_method = conf->resample_method; + + if (c->default_sink_name && + pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, 1) == NULL) { + pa_log_error("%s : Fatal error. Default sink name (%s) does not exist in name register.\n", __FILE__, c->default_sink_name); + retval = 1; + } else { + pa_log_info(__FILE__": Daemon startup complete.\n"); + if (pa_mainloop_run(mainloop, &retval) < 0) + retval = 1; + pa_log_info(__FILE__": Daemon shutdown initiated.\n"); + } + } + +#ifdef OS_IS_WIN32 + pa_mainloop_get_api(mainloop)->defer_free(defer); +#endif + + pa_core_free(c); + + pa_cpu_limit_done(); + pa_signal_done(); + pa_mainloop_free(mainloop); + + pa_log_info(__FILE__": Daemon terminated.\n"); + +finish: + + if (conf) + pa_daemon_conf_free(conf); + + if (valid_pid_file) + pa_pid_file_remove(); + + close_pipe(daemon_pipe); + +#ifdef OS_IS_WIN32 + WSACleanup(); +#endif + + lt_dlexit(); + + return retval; +} diff --git a/src/default.pa.in b/src/default.pa.in new file mode 100755 index 00000000..3aaeeaf0 --- /dev/null +++ b/src/default.pa.in @@ -0,0 +1,66 @@ +#!@POLYPAUDIO_BINARY@ -nF + +# +# This file is part of polypaudio. +# +# polypaudio is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# polypaudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with polypaudio; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + +# Load audio drivers statically + +#load-module module-alsa-sink +# load-module module-alsa-source device=plughw:1,0 +load-module module-oss device="/dev/dsp" sink_name=output source_name=input +#load-module module-oss-mmap device="/dev/dsp" sink_name=output source_name=input +load-module module-null-sink +#load-module module-pipe-sink + +# Load audio drivers automatically on access + +#add-autoload-sink output module-oss device="/dev/dsp" sink_name=output source_name=input +#add-autoload-source input module-oss device="/dev/dsp" sink_name=output source_name=input +#add-autoload-sink output module-oss-mmap device="/dev/dsp" sink_name=output source_name=input +#add-autoload-source input module-oss-mmap device="/dev/dsp" sink_name=output source_name=input +#add-autoload-sink output module-alsa-sink sink_name=output +#add-autoload-source input module-alsa-source source_name=input + +# Load several protocols +load-module module-esound-protocol-unix +#load-module module-esound-protocol-tcp +load-module module-native-protocol-unix +#load-module module-simple-protocol-tcp +#load-module module-cli-protocol-unix + +# Load the CLI module +load-module module-cli + +# Make some devices default +set-default-sink output +set-default-source input + +.nofail + +# Load something to the sample cache +load-sample x11-bell /usr/share/sounds/KDE_Notify.wav +load-sample-dir-lazy /usr/share/sounds/*.wav + +# Load X11 bell module +load-module module-x11-bell sample=x11-bell sink=output + +# Publish connection data in the X11 root window +load-module module-x11-publish + +#load-module module-pipe-source +#load-module module-pipe-sink diff --git a/src/default.pa.win32 b/src/default.pa.win32 new file mode 100644 index 00000000..3478adab --- /dev/null +++ b/src/default.pa.win32 @@ -0,0 +1,43 @@ +# +# This file is part of polypaudio. +# +# polypaudio is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# polypaudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with polypaudio; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + +# Load audio drivers statically + +load-module module-waveout sink_name=output source_name=input +load-module module-null-sink + +# Load audio drivers automatically on access + +#add-autoload-sink output module-waveout sink_name=output source_name=input +#add-autoload-source input module-waveout sink_name=output source_name=input + +# Load several protocols +#load-module module-esound-protocol-tcp +#load-module module-native-protocol-tcp +#load-module module-simple-protocol-tcp +#load-module module-cli-protocol-tcp + +# Make some devices default +set-default-sink output +set-default-source input + +.nofail + +# Load something to the sample cache +load-sample x11-bell %WINDIR%\Media\ding.wav +load-sample-dir-lazy %WINDIR%\Media\*.wav diff --git a/src/depmod.py b/src/depmod.py new file mode 100755 index 00000000..7bb223b1 --- /dev/null +++ b/src/depmod.py @@ -0,0 +1,73 @@ +#!/usr/bin/python +# $Id$ +# +# This file is part of polypaudio. +# +# polypaudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# polypaudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with polypaudio; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + +import sys, os, string + +exported_symbols = {} +imported_symbols = {} + +for fn in sys.argv[1:]: + f = os.popen("nm '%s'" % fn, "r") + + imported_symbols[fn] = [] + + for line in f: + sym_address = line[:7].strip() + sym_type = line[9].strip() + sym_name = line[11:].strip() + + if sym_name in ('_fini', '_init'): + continue + + if sym_type in ('T', 'B', 'R', 'D' 'G', 'S', 'D'): + if exported_symbols.has_key(sym_name): + sys.stderr.write("CONFLICT: %s defined in both '%s' and '%s'.\n" % (sym_name, fn, exported_symbols[sym_name])) + else: + exported_symbols[sym_name] = fn + elif sym_type in ('U',): + if sym_name[:3] == 'pa_': + imported_symbols[fn].append(sym_name) + + f.close() + +dependencies = {} +unresolved_symbols = {} + +for fn in imported_symbols: + dependencies[fn] = [] + + for sym in imported_symbols[fn]: + if exported_symbols.has_key(sym): + if exported_symbols[sym] not in dependencies[fn]: + dependencies[fn].append(exported_symbols[sym]) + else: + if unresolved_symbols.has_key(sym): + unresolved_symbols[sym].append(fn) + else: + unresolved_symbols[sym] = [fn] + +for sym, files in unresolved_symbols.iteritems(): + print "WARNING: Unresolved symbol '%s' in %s" % (sym, `files`) + +k = dependencies.keys() +k.sort() +for fn in k: + dependencies[fn].sort() + print "%s: %s" % (fn, string.join(dependencies[fn], " ")) diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c new file mode 100644 index 00000000..48e90e9f --- /dev/null +++ b/src/modules/module-alsa-sink.c @@ -0,0 +1,292 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#ifdef HAVE_SYS_POLL_H +#include +#else +#include "poll.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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= device= format= channels= rate= fragments= fragment_size=") + +struct userdata { + snd_pcm_t *pcm_handle; + pa_sink *sink; + pa_io_event **io_events; + unsigned n_io_events; + + size_t frame_size, fragment_size; + pa_memchunk memchunk, silence; + pa_module *module; +}; + +static const char* const valid_modargs[] = { + "device", + "sink_name", + "format", + "channels", + "rate", + "fragments", + "fragment_size", + NULL +}; + +#define DEFAULT_SINK_NAME "alsa_output" +#define DEFAULT_DEVICE "default" + +static void update_usage(struct userdata *u) { + pa_module_set_used(u->module, + (u->sink ? pa_idxset_size(u->sink->inputs) : 0) + + (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0)); +} + +static void xrun_recovery(struct userdata *u) { + assert(u); + + pa_log(__FILE__": *** ALSA-XRUN (playback) ***\n"); + + if (snd_pcm_prepare(u->pcm_handle) < 0) + pa_log(__FILE__": snd_pcm_prepare() failed\n"); +} + +static void do_write(struct userdata *u) { + assert(u); + + update_usage(u); + + for (;;) { + pa_memchunk *memchunk = NULL; + snd_pcm_sframes_t frames; + + if (u->memchunk.memblock) + memchunk = &u->memchunk; + else { + if (pa_sink_render(u->sink, u->fragment_size, &u->memchunk) < 0) + memchunk = &u->silence; + else + memchunk = &u->memchunk; + } + + assert(memchunk->memblock && memchunk->memblock->data && memchunk->length && memchunk->memblock->length && (memchunk->length % u->frame_size) == 0); + + if ((frames = snd_pcm_writei(u->pcm_handle, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length / u->frame_size)) < 0) { + if (frames == -EAGAIN) + return; + + if (frames == -EPIPE) { + xrun_recovery(u); + continue; + } + + pa_log(__FILE__": snd_pcm_writei() failed\n"); + return; + } + + if (memchunk == &u->memchunk) { + size_t l = frames * u->frame_size; + memchunk->index += l; + memchunk->length -= l; + + if (memchunk->length == 0) { + pa_memblock_unref(memchunk->memblock); + memchunk->memblock = NULL; + memchunk->index = memchunk->length = 0; + } + } + + break; + } +} + +static void io_callback(pa_mainloop_api*a, pa_io_event *e, PA_GCC_UNUSED int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { + struct userdata *u = userdata; + assert(u && a && e); + + if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN) + xrun_recovery(u); + + do_write(u); +} + +static pa_usec_t sink_get_latency_cb(pa_sink *s) { + pa_usec_t r = 0; + struct userdata *u = s->userdata; + snd_pcm_sframes_t frames; + assert(s && u && u->sink); + + if (snd_pcm_delay(u->pcm_handle, &frames) < 0) { + pa_log(__FILE__": failed to get delay\n"); + s->get_latency = NULL; + return 0; + } + + if (frames < 0) + frames = 0; + + r += pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec); + + if (u->memchunk.memblock) + r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec); + + return r; +} + +int pa__init(pa_core *c, pa_module*m) { + pa_modargs *ma = NULL; + int ret = -1; + struct userdata *u = NULL; + const char *dev; + pa_sample_spec ss; + uint32_t periods, fragsize; + snd_pcm_uframes_t period_size; + size_t frame_size; + int err; + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments\n"); + goto fail; + } + + ss = c->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &ss) < 0) { + pa_log(__FILE__": failed to parse sample specification\n"); + goto fail; + } + frame_size = pa_frame_size(&ss); + + periods = 8; + fragsize = 1024; + if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) { + pa_log(__FILE__": failed to parse buffer metrics\n"); + goto fail; + } + period_size = fragsize; + + u = pa_xmalloc0(sizeof(struct userdata)); + m->userdata = u; + u->module = m; + + snd_config_update_free_global(); + if ((err = snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { + pa_log(__FILE__": Error opening PCM device %s: %s\n", dev, snd_strerror(err)); + goto fail; + } + + if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &period_size)) < 0) { + pa_log(__FILE__": Failed to set hardware parameters: %s\n", snd_strerror(err)); + goto fail; + } + + u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL); + assert(u->sink); + + u->sink->get_latency = sink_get_latency_cb; + u->sink->userdata = u; + pa_sink_set_owner(u->sink, m); + u->sink->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev); + + if (pa_create_io_events(u->pcm_handle, c->mainloop, &u->io_events, &u->n_io_events, io_callback, u) < 0) { + pa_log(__FILE__": failed to obtain file descriptors\n"); + goto fail; + } + + u->frame_size = frame_size; + u->fragment_size = period_size; + + pa_log_info(__FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size); + + u->silence.memblock = pa_memblock_new(u->silence.length = u->fragment_size, c->memblock_stat); + assert(u->silence.memblock); + pa_silence_memblock(u->silence.memblock, &ss); + u->silence.index = 0; + + u->memchunk.memblock = NULL; + u->memchunk.index = u->memchunk.length = 0; + + ret = 0; + +finish: + if (ma) + pa_modargs_free(ma); + + return ret; + +fail: + + if (u) + pa__done(c, m); + + goto finish; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata *u; + assert(c && m); + + if (!(u = m->userdata)) + return; + + if (u->sink) { + pa_sink_disconnect(u->sink); + pa_sink_unref(u->sink); + } + + if (u->io_events) + pa_free_io_events(c->mainloop, u->io_events, u->n_io_events); + + if (u->pcm_handle) { + snd_pcm_drop(u->pcm_handle); + snd_pcm_close(u->pcm_handle); + } + + if (u->memchunk.memblock) + pa_memblock_unref(u->memchunk.memblock); + if (u->silence.memblock) + pa_memblock_unref(u->silence.memblock); + + pa_xfree(u); +} + diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c new file mode 100644 index 00000000..f03e51ad --- /dev/null +++ b/src/modules/module-alsa-source.c @@ -0,0 +1,278 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#ifdef HAVE_SYS_POLL_H +#include +#else +#include "poll.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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= device= format= channels= rate= fragments= fragment_size=") + +struct userdata { + snd_pcm_t *pcm_handle; + pa_source *source; + pa_io_event **io_events; + unsigned n_io_events; + + size_t frame_size, fragment_size; + pa_memchunk memchunk; + pa_module *module; +}; + +static const char* const valid_modargs[] = { + "device", + "source_name", + "channels", + "rate", + "format", + "fragments", + "fragment_size", + NULL +}; + +#define DEFAULT_SOURCE_NAME "alsa_input" +#define DEFAULT_DEVICE "hw:0,0" + +static void update_usage(struct userdata *u) { + pa_module_set_used(u->module, + (u->source ? pa_idxset_size(u->source->outputs) : 0)); +} + +static void xrun_recovery(struct userdata *u) { + assert(u); + + pa_log(__FILE__": *** ALSA-XRUN (capture) ***\n"); + + if (snd_pcm_prepare(u->pcm_handle) < 0) + pa_log(__FILE__": snd_pcm_prepare() failed\n"); +} + +static void do_read(struct userdata *u) { + assert(u); + + update_usage(u); + + for (;;) { + pa_memchunk post_memchunk; + snd_pcm_sframes_t frames; + size_t l; + + if (!u->memchunk.memblock) { + u->memchunk.memblock = pa_memblock_new(u->memchunk.length = u->fragment_size, u->source->core->memblock_stat); + u->memchunk.index = 0; + } + + assert(u->memchunk.memblock && u->memchunk.memblock->data && u->memchunk.length && u->memchunk.memblock->length && (u->memchunk.length % u->frame_size) == 0); + + if ((frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) { + if (frames == -EAGAIN) + return; + + if (frames == -EPIPE) { + xrun_recovery(u); + continue; + } + + pa_log(__FILE__": snd_pcm_readi() failed: %s\n", strerror(-frames)); + return; + } + + l = frames * u->frame_size; + + post_memchunk = u->memchunk; + post_memchunk.length = l; + + pa_source_post(u->source, &post_memchunk); + + u->memchunk.index += l; + u->memchunk.length -= l; + + if (u->memchunk.length == 0) { + pa_memblock_unref(u->memchunk.memblock); + u->memchunk.memblock = NULL; + u->memchunk.index = u->memchunk.length = 0; + } + + break; + } +} + +static void io_callback(pa_mainloop_api*a, pa_io_event *e, PA_GCC_UNUSED int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { + struct userdata *u = userdata; + assert(u && a && e); + + if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN) + xrun_recovery(u); + + do_read(u); +} + +static pa_usec_t source_get_latency_cb(pa_source *s) { + struct userdata *u = s->userdata; + snd_pcm_sframes_t frames; + assert(s && u && u->source); + + if (snd_pcm_delay(u->pcm_handle, &frames) < 0) { + pa_log(__FILE__": failed to get delay\n"); + s->get_latency = NULL; + return 0; + } + + return pa_bytes_to_usec(frames * u->frame_size, &s->sample_spec); +} + +int pa__init(pa_core *c, pa_module*m) { + pa_modargs *ma = NULL; + int ret = -1; + struct userdata *u = NULL; + const char *dev; + pa_sample_spec ss; + unsigned periods, fragsize; + snd_pcm_uframes_t period_size; + size_t frame_size; + int err; + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments\n"); + goto fail; + } + + ss = c->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &ss) < 0) { + pa_log(__FILE__": failed to parse sample specification\n"); + goto fail; + } + frame_size = pa_frame_size(&ss); + + periods = 12; + fragsize = 1024; + if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) { + pa_log(__FILE__": failed to parse buffer metrics\n"); + goto fail; + } + period_size = fragsize; + + u = pa_xmalloc0(sizeof(struct userdata)); + m->userdata = u; + u->module = m; + + snd_config_update_free_global(); + if ((err = snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) { + pa_log(__FILE__": Error opening PCM device %s: %s\n", dev, snd_strerror(err)); + goto fail; + } + + if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &period_size)) < 0) { + pa_log(__FILE__": Failed to set hardware parameters: %s\n", snd_strerror(err)); + goto fail; + } + + u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL); + assert(u->source); + + u->source->userdata = u; + u->source->get_latency = source_get_latency_cb; + pa_source_set_owner(u->source, m); + u->source->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev); + + if (pa_create_io_events(u->pcm_handle, c->mainloop, &u->io_events, &u->n_io_events, io_callback, u) < 0) { + pa_log(__FILE__": failed to obtain file descriptors\n"); + goto fail; + } + + u->frame_size = frame_size; + u->fragment_size = period_size; + + pa_log(__FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size); + + u->memchunk.memblock = NULL; + u->memchunk.index = u->memchunk.length = 0; + + snd_pcm_start(u->pcm_handle); + + ret = 0; + +finish: + if (ma) + pa_modargs_free(ma); + + return ret; + +fail: + + if (u) + pa__done(c, m); + + goto finish; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata *u; + assert(c && m); + + if (!(u = m->userdata)) + return; + + if (u->source) { + pa_source_disconnect(u->source); + pa_source_unref(u->source); + } + + if (u->io_events) + pa_free_io_events(c->mainloop, u->io_events, u->n_io_events); + + if (u->pcm_handle) { + snd_pcm_drop(u->pcm_handle); + snd_pcm_close(u->pcm_handle); + } + + if (u->memchunk.memblock) + pa_memblock_unref(u->memchunk.memblock); + + pa_xfree(u); +} + diff --git a/src/modules/module-cli.c b/src/modules/module-cli.c new file mode 100644 index 00000000..c782ff8d --- /dev/null +++ b/src/modules/module-cli.c @@ -0,0 +1,88 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "module-cli-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("Command line interface") +PA_MODULE_VERSION(PACKAGE_VERSION) +PA_MODULE_USAGE("No arguments") + +static void eof_cb(pa_cli*c, void *userdata) { + pa_module *m = userdata; + assert(c && m); + + pa_module_unload_request(m); +} + +int pa__init(pa_core *c, pa_module*m) { + pa_iochannel *io; + assert(c && m); + + if (c->running_as_daemon) { + pa_log_info(__FILE__": Running as daemon so won't load this module.\n"); + return 0; + } + + if (m->argument) { + pa_log(__FILE__": module doesn't accept arguments.\n"); + return -1; + } + + if (pa_stdio_acquire() < 0) { + pa_log(__FILE__": STDIN/STDUSE already in use.\n"); + return -1; + } + + io = pa_iochannel_new(c->mainloop, STDIN_FILENO, STDOUT_FILENO); + assert(io); + pa_iochannel_set_noclose(io, 1); + + m->userdata = pa_cli_new(c, io, m); + assert(m->userdata); + + pa_cli_set_eof_callback(m->userdata, eof_cb, m); + + return 0; +} + +void pa__done(pa_core *c, pa_module*m) { + assert(c && m); + + if (c->running_as_daemon == 0) { + pa_cli_free(m->userdata); + pa_stdio_release(); + } +} diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c new file mode 100644 index 00000000..aabb8f28 --- /dev/null +++ b/src/modules/module-combine.c @@ -0,0 +1,394 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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= master= slaves= adjust_time= resample_method=") + +#define DEFAULT_SINK_NAME "combined" +#define MEMBLOCKQ_MAXLENGTH (1024*170) +#define RENDER_SIZE (1024*10) + +#define DEFAULT_ADJUST_TIME 20 + +static const char* const valid_modargs[] = { + "sink_name", + "master", + "slaves", + "adjust_time", + "resample_method", + NULL +}; + +struct output { + struct userdata *userdata; + pa_sink_input *sink_input; + size_t counter; + pa_memblockq *memblockq; + pa_usec_t total_latency; + PA_LLIST_FIELDS(struct output); +}; + +struct userdata { + pa_module *module; + pa_core *core; + pa_sink *sink; + unsigned n_outputs; + struct output *master; + pa_time_event *time_event; + uint32_t adjust_time; + + PA_LLIST_HEAD(struct output, outputs); +}; + +static void output_free(struct output *o); +static void clear_up(struct userdata *u); + +static void update_usage(struct userdata *u) { + pa_module_set_used(u->module, + (u->sink ? pa_idxset_size(u->sink->inputs) : 0) + + (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0)); +} + + +static void adjust_rates(struct userdata *u) { + struct output *o; + pa_usec_t max_sink_latency = 0, min_total_latency = (pa_usec_t) -1, target_latency; + uint32_t base_rate; + assert(u && u->sink); + + for (o = u->outputs; o; o = o->next) { + uint32_t sink_latency = o->sink_input->sink ? pa_sink_get_latency(o->sink_input->sink) : 0; + + o->total_latency = sink_latency + pa_sink_input_get_latency(o->sink_input); + + if (sink_latency > max_sink_latency) + max_sink_latency = sink_latency; + + if (o->total_latency < min_total_latency) + min_total_latency = o->total_latency; + } + + assert(min_total_latency != (pa_usec_t) -1); + + target_latency = max_sink_latency > min_total_latency ? max_sink_latency : min_total_latency; + + pa_log_info(__FILE__": [%s] target latency is %0.0f usec.\n", u->sink->name, (float) target_latency); + + base_rate = u->sink->sample_spec.rate; + + for (o = u->outputs; o; o = o->next) { + uint32_t r = base_rate; + + if (o->total_latency < target_latency) + r -= (uint32_t) (((((double) target_latency - o->total_latency))/u->adjust_time)*r/ 1000000); + else if (o->total_latency > target_latency) + r += (uint32_t) (((((double) o->total_latency - target_latency))/u->adjust_time)*r/ 1000000); + + if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1)) + pa_log_warn(__FILE__": [%s] sample rates too different, not adjusting (%u vs. %u).\n", o->sink_input->name, base_rate, r); + else { + pa_log_info(__FILE__": [%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.\n", o->sink_input->name, r, (double) r / base_rate, (float) o->total_latency); + pa_sink_input_set_rate(o->sink_input, r); + } + } +} + +static void request_memblock(struct userdata *u) { + pa_memchunk chunk; + struct output *o; + assert(u && u->sink); + + update_usage(u); + + if (pa_sink_render(u->sink, RENDER_SIZE, &chunk) < 0) + return; + + for (o = u->outputs; o; o = o->next) + pa_memblockq_push_align(o->memblockq, &chunk, 0); + + pa_memblock_unref(chunk.memblock); +} + +static void time_callback(pa_mainloop_api*a, pa_time_event* e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { + struct userdata *u = userdata; + struct timeval n; + assert(u && a && u->time_event == e); + + adjust_rates(u); + + pa_gettimeofday(&n); + n.tv_sec += u->adjust_time; + u->sink->core->mainloop->time_restart(e, &n); +} + +static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) { + struct output *o = i->userdata; + assert(i && o && o->sink_input && chunk); + + if (pa_memblockq_peek(o->memblockq, chunk) >= 0) + return 0; + + /* Try harder */ + request_memblock(o->userdata); + + return pa_memblockq_peek(o->memblockq, chunk); +} + +static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { + struct output *o = i->userdata; + assert(i && o && o->sink_input && chunk && length); + + pa_memblockq_drop(o->memblockq, chunk, length); + o->counter += length; +} + +static void sink_input_kill_cb(pa_sink_input *i) { + struct output *o = i->userdata; + assert(i && o && o->sink_input); + pa_module_unload_request(o->userdata->module); + clear_up(o->userdata); +} + +static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) { + struct output *o = i->userdata; + assert(i && o && o->sink_input); + + return pa_bytes_to_usec(pa_memblockq_get_length(o->memblockq), &i->sample_spec); +} + +static pa_usec_t sink_get_latency_cb(pa_sink *s) { + struct userdata *u = s->userdata; + assert(s && u && u->sink && u->master); + + return pa_sink_input_get_latency(u->master->sink_input); +} + +static struct output *output_new(struct userdata *u, pa_sink *sink, int resample_method) { + struct output *o = NULL; + char t[256]; + assert(u && sink && u->sink); + + o = pa_xmalloc(sizeof(struct output)); + o->userdata = u; + + o->counter = 0; + o->memblockq = pa_memblockq_new(MEMBLOCKQ_MAXLENGTH, MEMBLOCKQ_MAXLENGTH, pa_frame_size(&u->sink->sample_spec), 0, 0, sink->core->memblock_stat); + + snprintf(t, sizeof(t), "%s: output #%u", u->sink->name, u->n_outputs+1); + if (!(o->sink_input = pa_sink_input_new(sink, __FILE__, t, &u->sink->sample_spec, &u->sink->channel_map, 1, resample_method))) + goto fail; + + o->sink_input->get_latency = sink_input_get_latency_cb; + o->sink_input->peek = sink_input_peek_cb; + o->sink_input->drop = sink_input_drop_cb; + o->sink_input->kill = sink_input_kill_cb; + o->sink_input->userdata = o; + o->sink_input->owner = u->module; + + PA_LLIST_PREPEND(struct output, u->outputs, o); + u->n_outputs++; + return o; + +fail: + + if (o) { + if (o->sink_input) { + pa_sink_input_disconnect(o->sink_input); + pa_sink_input_unref(o->sink_input); + } + + if (o->memblockq) + pa_memblockq_free(o->memblockq); + + pa_xfree(o); + } + + return NULL; +} + +static void output_free(struct output *o) { + assert(o); + PA_LLIST_REMOVE(struct output, o->userdata->outputs, o); + o->userdata->n_outputs--; + pa_memblockq_free(o->memblockq); + pa_sink_input_disconnect(o->sink_input); + pa_sink_input_unref(o->sink_input); + pa_xfree(o); +} + +static void clear_up(struct userdata *u) { + struct output *o; + assert(u); + + if (u->time_event) { + u->core->mainloop->time_free(u->time_event); + u->time_event = NULL; + } + + while ((o = u->outputs)) + output_free(o); + + u->master = NULL; + + if (u->sink) { + pa_sink_disconnect(u->sink); + pa_sink_unref(u->sink); + u->sink = NULL; + } +} + +int pa__init(pa_core *c, pa_module*m) { + struct userdata *u; + pa_modargs *ma = NULL; + const char *master_name, *slaves, *rm; + pa_sink *master_sink; + char *n = NULL; + const char*split_state; + struct timeval tv; + int resample_method = -1; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments\n"); + goto fail; + } + + if ((rm = pa_modargs_get_value(ma, "resample_method", NULL))) { + if ((resample_method = pa_parse_resample_method(rm)) < 0) { + pa_log(__FILE__": invalid resample method '%s'\n", rm); + goto fail; + } + } + + u = pa_xmalloc(sizeof(struct userdata)); + m->userdata = u; + u->sink = NULL; + u->n_outputs = 0; + u->master = NULL; + u->module = m; + u->core = c; + u->time_event = NULL; + u->adjust_time = DEFAULT_ADJUST_TIME; + PA_LLIST_HEAD_INIT(struct output, u->outputs); + + if (pa_modargs_get_value_u32(ma, "adjust_time", &u->adjust_time) < 0) { + pa_log(__FILE__": failed to parse adjust_time value\n"); + goto fail; + } + + if (!(master_name = pa_modargs_get_value(ma, "master", NULL)) || !(slaves = pa_modargs_get_value(ma, "slaves", NULL))) { + pa_log(__FILE__": no master or slave sinks specified\n"); + goto fail; + } + + if (!(master_sink = pa_namereg_get(c, master_name, PA_NAMEREG_SINK, 1))) { + pa_log(__FILE__": invalid master sink '%s'\n", master_name); + goto fail; + } + + if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &master_sink->sample_spec, &master_sink->channel_map))) { + pa_log(__FILE__": failed to create sink\n"); + goto fail; + } + + pa_sink_set_owner(u->sink, m); + u->sink->description = pa_sprintf_malloc("Combined sink"); + u->sink->get_latency = sink_get_latency_cb; + u->sink->userdata = u; + + if (!(u->master = output_new(u, master_sink, resample_method))) { + pa_log(__FILE__": failed to create master sink input on sink '%s'.\n", u->sink->name); + goto fail; + } + + split_state = NULL; + while ((n = pa_split(slaves, ",", &split_state))) { + pa_sink *slave_sink; + + if (!(slave_sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) { + pa_log(__FILE__": invalid slave sink '%s'\n", n); + goto fail; + } + + pa_xfree(n); + + if (!output_new(u, slave_sink, resample_method)) { + pa_log(__FILE__": failed to create slave sink input on sink '%s'.\n", slave_sink->name); + goto fail; + } + } + + if (u->n_outputs <= 1) + pa_log_warn(__FILE__": WARNING: no slave sinks specified.\n"); + + if (u->adjust_time > 0) { + pa_gettimeofday(&tv); + tv.tv_sec += u->adjust_time; + u->time_event = c->mainloop->time_new(c->mainloop, &tv, time_callback, u); + } + + pa_modargs_free(ma); + return 0; + +fail: + pa_xfree(n); + + if (ma) + pa_modargs_free(ma); + + pa__done(c, m); + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata *u; + assert(c && m); + + if (!(u = m->userdata)) + return; + + clear_up(u); + pa_xfree(u); +} + + diff --git a/src/modules/module-defs.h.m4 b/src/modules/module-defs.h.m4 new file mode 100644 index 00000000..8f10d659 --- /dev/null +++ b/src/modules/module-defs.h.m4 @@ -0,0 +1,29 @@ +dnl $Id$ +changecom(`/*', `*/')dnl +define(`module_name', patsubst(patsubst(patsubst(fname, `-symdef.h$'), `^.*/'), `[^0-9a-zA-Z]', `_'))dnl +define(`c_symbol', patsubst(module_name, `[^0-9a-zA-Z]', `_'))dnl +define(`c_macro', patsubst(module_name, `[^0-9a-zA-Z]', `'))dnl +define(`incmacro', `foo'c_macro`symdeffoo')dnl +define(`gen_symbol', `#define $1 'module_name`_LTX_$1')dnl +#ifndef incmacro +#define incmacro + +#include +#include + +gen_symbol(pa__init) +gen_symbol(pa__done) +gen_symbol(pa__get_author) +gen_symbol(pa__get_description) +gen_symbol(pa__get_usage) +gen_symbol(pa__get_version) + +int pa__init(struct pa_core *c, struct pa_module*m); +void pa__done(struct pa_core *c, struct pa_module*m); + +const char* pa__get_author(void); +const char* pa__get_description(void); +const char* pa__get_usage(void); +const char* pa__get_version(void); + +#endif diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c new file mode 100644 index 00000000..e325b22c --- /dev/null +++ b/src/modules/module-detect.c @@ -0,0 +1,227 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "module-detect-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers") +PA_MODULE_VERSION(PACKAGE_VERSION) +PA_MODULE_USAGE("just-one=") + +static const char *endswith(const char *haystack, const char *needle) { + size_t l, m; + const char *p; + + if ((l = strlen(haystack)) < (m = strlen(needle))) + return NULL; + + if (strcmp(p = haystack + l - m, needle)) + return NULL; + + return p; +} + +#ifdef HAVE_ALSA +static int detect_alsa(pa_core *c, int just_one) { + FILE *f; + int n = 0, n_sink = 0, n_source = 0; + + if (!(f = fopen("/proc/asound/devices", "r"))) { + + if (errno != ENOENT) + pa_log_error(__FILE__": open(\"/proc/asound/devices\") failed: %s\n", strerror(errno)); + + return -1; + } + + while (!feof(f)) { + char line[64], args[64]; + unsigned device, subdevice; + int is_sink; + + if (!fgets(line, sizeof(line), f)) + break; + + line[strcspn(line, "\r\n")] = 0; + + if (endswith(line, "digital audio playback")) + is_sink = 1; + else if (endswith(line, "digital audio capture")) + is_sink = 0; + else + continue; + + if (just_one && is_sink && n_sink >= 1) + continue; + + if (just_one && !is_sink && n_source >= 1) + continue; + + if (sscanf(line, " %*i: [%u- %u]: ", &device, &subdevice) != 2) + continue; + + /* Only one sink per device */ + if (subdevice != 0) + continue; + + snprintf(args, sizeof(args), "device=hw:%u,0", device); + if (!pa_module_load(c, is_sink ? "module-alsa-sink" : "module-alsa-source", args)) + continue; + + n++; + + if (is_sink) + n_sink++; + else + n_source++; + } + + fclose(f); + + return n; +} +#endif + +#ifdef HAVE_OSS +static int detect_oss(pa_core *c, int just_one) { + FILE *f; + int n = 0, b = 0; + + if (!(f = fopen("/dev/sndstat", "r")) && + !(f = fopen("/proc/sndstat", "r")) && + !(f = fopen("/proc/asound/oss/sndstat", "r"))) { + + if (errno != ENOENT) + pa_log_error(__FILE__": failed to open OSS sndstat device: %s\n", strerror(errno)); + + return -1; + } + + while (!feof(f)) { + char line[64], args[64]; + unsigned device; + + if (!fgets(line, sizeof(line), f)) + break; + + line[strcspn(line, "\r\n")] = 0; + + if (!b) { + b = strcmp(line, "Audio devices:") == 0; + continue; + } + + if (line[0] == 0) + break; + + if (sscanf(line, "%u: ", &device) != 1) + continue; + + if (device == 0) + snprintf(args, sizeof(args), "device=/dev/dsp"); + else + snprintf(args, sizeof(args), "device=/dev/dsp%u", device); + + if (!pa_module_load(c, "module-oss", args)) + continue; + + n++; + + if (just_one) + break; + } + + fclose(f); + return n; +} +#endif + +int pa__init(pa_core *c, pa_module*m) { + int just_one = 0, n = 0; + pa_modargs *ma; + + static const char* const valid_modargs[] = { + "just-one", + NULL + }; + + assert(c); + assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": Failed to parse module arguments\n"); + goto fail; + } + + if (pa_modargs_get_value_boolean(ma, "just-one", &just_one) < 0) { + pa_log(__FILE__": just_one= expects a boolean argument.\n"); + goto fail; + } + +#if HAVE_ALSA + if ((n = detect_alsa(c, just_one)) <= 0) +#endif +#if HAVE_OSS + if ((n = detect_oss(c, just_one)) <= 0) +#endif + { + pa_log_warn(__FILE__": failed to detect any sound hardware.\n"); + goto fail; + } + + pa_log_info(__FILE__": loaded %i modules.\n", n); + + /* We were successful and can unload ourselves now. */ + pa_module_unload_request(m); + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + return -1; +} + + +void pa__done(pa_core *c, pa_module*m) { + /* NOP */ +} + diff --git a/src/modules/module-esound-compat-spawnfd.c b/src/modules/module-esound-compat-spawnfd.c new file mode 100644 index 00000000..5c656be9 --- /dev/null +++ b/src/modules/module-esound-compat-spawnfd.c @@ -0,0 +1,81 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "module-esound-compat-spawnfd-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnfd emulation") +PA_MODULE_USAGE("fd=") +PA_MODULE_VERSION(PACKAGE_VERSION) + +static const char* const valid_modargs[] = { + "fd", + NULL, +}; + +int pa__init(pa_core *c, pa_module*m) { + pa_modargs *ma = NULL; + int ret = -1, fd = -1; + char x = 1; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs)) || + pa_modargs_get_value_s32(ma, "fd", &fd) < 0 || + fd < 0) { + pa_log(__FILE__": Failed to parse module arguments\n"); + goto finish; + } + + if (pa_loop_write(fd, &x, sizeof(x)) != sizeof(x)) + pa_log(__FILE__": WARNING: write(%u, 1, 1) failed: %s\n", fd, strerror(errno)); + + close(fd); + + pa_module_unload_request(m); + + ret = 0; + +finish: + if (ma) + pa_modargs_free(ma); + + return ret; +} + +void pa__done(pa_core *c, pa_module*m) { + assert(c && m); +} + + diff --git a/src/modules/module-esound-compat-spawnpid.c b/src/modules/module-esound-compat-spawnpid.c new file mode 100644 index 00000000..5daa1297 --- /dev/null +++ b/src/modules/module-esound-compat-spawnpid.c @@ -0,0 +1,79 @@ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "module-esound-compat-spawnpid-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("ESOUND compatibility module: -spawnpid emulation") +PA_MODULE_USAGE("pid=") +PA_MODULE_VERSION(PACKAGE_VERSION) + +static const char* const valid_modargs[] = { + "pid", + NULL, +}; + +int pa__init(pa_core *c, pa_module*m) { + pa_modargs *ma = NULL; + int ret = -1; + uint32_t pid = 0; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs)) || + pa_modargs_get_value_u32(ma, "pid", &pid) < 0 || + !pid) { + pa_log(__FILE__": Failed to parse module arguments\n"); + goto finish; + } + + if (kill(pid, SIGUSR1) < 0) + pa_log(__FILE__": WARNING: kill(%u) failed: %s\n", pid, strerror(errno)); + + pa_module_unload_request(m); + + ret = 0; + +finish: + if (ma) + pa_modargs_free(ma); + + return ret; +} + +void pa__done(pa_core *c, pa_module*m) { + assert(c && m); +} + + diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c new file mode 100644 index 00000000..4f724811 --- /dev/null +++ b/src/modules/module-esound-sink.c @@ -0,0 +1,427 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "module-esound-sink-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("ESOUND Sink") +PA_MODULE_VERSION(PACKAGE_VERSION) +PA_MODULE_USAGE("sink_name= server=
cookie= format= channels= rate=") + +#define DEFAULT_SINK_NAME "esound_output" + +struct userdata { + pa_core *core; + + pa_sink *sink; + pa_iochannel *io; + pa_socket_client *client; + + pa_defer_event *defer_event; + + pa_memchunk memchunk; + pa_module *module; + + void *write_data; + size_t write_length, write_index; + + void *read_data; + size_t read_length, read_index; + + enum { STATE_AUTH, STATE_LATENCY, STATE_RUNNING, STATE_DEAD } state; + + pa_usec_t latency; + + esd_format_t format; + int32_t rate; +}; + +static const char* const valid_modargs[] = { + "server", + "cookie", + "rate", + "format", + "channels", + "sink_name", + NULL +}; + +static void cancel(struct userdata *u) { + assert(u); + + u->state = STATE_DEAD; + + if (u->io) { + pa_iochannel_free(u->io); + u->io = NULL; + } + + if (u->defer_event) { + u->core->mainloop->defer_free(u->defer_event); + u->defer_event = NULL; + } + + if (u->sink) { + pa_sink_disconnect(u->sink); + pa_sink_unref(u->sink); + u->sink = NULL; + } + + if (u->module) { + pa_module_unload_request(u->module); + u->module = NULL; + } +} + +static int do_write(struct userdata *u) { + ssize_t r; + assert(u); + + if (!pa_iochannel_is_writable(u->io)) + return 0; + + if (u->write_data) { + assert(u->write_index < u->write_length); + + if ((r = pa_iochannel_write(u->io, (uint8_t*) u->write_data + u->write_index, u->write_length - u->write_index)) <= 0) { + pa_log(__FILE__": write() failed: %s\n", strerror(errno)); + return -1; + } + + u->write_index += r; + assert(u->write_index <= u->write_length); + + if (u->write_index == u->write_length) { + free(u->write_data); + u->write_data = NULL; + u->write_index = u->write_length = 0; + } + } else if (u->state == STATE_RUNNING) { + pa_module_set_used(u->module, pa_idxset_size(u->sink->inputs) + pa_idxset_size(u->sink->monitor_source->outputs)); + + if (!u->memchunk.length) + if (pa_sink_render(u->sink, 8192, &u->memchunk) < 0) + return 0; + + assert(u->memchunk.memblock && u->memchunk.length); + + if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) { + pa_log(__FILE__": write() failed: %s\n", strerror(errno)); + return -1; + } + + u->memchunk.index += r; + u->memchunk.length -= r; + + if (u->memchunk.length <= 0) { + pa_memblock_unref(u->memchunk.memblock); + u->memchunk.memblock = NULL; + } + } + + return 0; +} + +static int handle_response(struct userdata *u) { + assert(u); + + switch (u->state) { + case STATE_AUTH: + assert(u->read_length == sizeof(int32_t)); + + /* Process auth data */ + if (!*(int32_t*) u->read_data) { + pa_log(__FILE__": Authentication failed: %s\n", strerror(errno)); + return -1; + } + + /* Request latency data */ + assert(!u->write_data); + *(int32_t*) (u->write_data = pa_xmalloc(u->write_length = sizeof(int32_t))) = ESD_PROTO_LATENCY; + + u->write_index = 0; + u->state = STATE_LATENCY; + + /* Space for next response */ + assert(u->read_length >= sizeof(int32_t)); + u->read_index = 0; + u->read_length = sizeof(int32_t); + + break; + + case STATE_LATENCY: { + int32_t *p; + assert(u->read_length == sizeof(int32_t)); + + /* Process latency info */ + u->latency = (pa_usec_t) ((double) (*(int32_t*) u->read_data) * 1000000 / 44100); + if (u->latency > 10000000) { + pa_log(__FILE__": WARNING! Invalid latency information received from server\n"); + u->latency = 0; + } + + /* Create stream */ + assert(!u->write_data); + p = u->write_data = pa_xmalloc0(u->write_length = sizeof(int32_t)*3+ESD_NAME_MAX); + *(p++) = ESD_PROTO_STREAM_PLAY; + *(p++) = u->format; + *(p++) = u->rate; + pa_strlcpy((char*) p, "Polypaudio Tunnel", ESD_NAME_MAX); + + u->write_index = 0; + u->state = STATE_RUNNING; + + /* Don't read any further */ + pa_xfree(u->read_data); + u->read_data = NULL; + u->read_index = u->read_length = 0; + + break; + } + + default: + abort(); + } + + return 0; +} + +static int do_read(struct userdata *u) { + assert(u); + + if (!pa_iochannel_is_readable(u->io)) + return 0; + + if (u->state == STATE_AUTH || u->state == STATE_LATENCY) { + ssize_t r; + + if (!u->read_data) + return 0; + + assert(u->read_index < u->read_length); + + if ((r = pa_iochannel_read(u->io, (uint8_t*) u->read_data + u->read_index, u->read_length - u->read_index)) <= 0) { + pa_log(__FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF"); + cancel(u); + return -1; + } + + u->read_index += r; + assert(u->read_index <= u->read_length); + + if (u->read_index == u->read_length) + return handle_response(u); + } + + return 0; +} + +static void do_work(struct userdata *u) { + assert(u); + + u->core->mainloop->defer_enable(u->defer_event, 0); + + if (do_read(u) < 0 || do_write(u) < 0) + cancel(u); +} + +static void notify_cb(pa_sink*s) { + struct userdata *u = s->userdata; + assert(s && u); + + if (pa_iochannel_is_writable(u->io)) + u->core->mainloop->defer_enable(u->defer_event, 1); +} + +static pa_usec_t get_latency_cb(pa_sink *s) { + struct userdata *u = s->userdata; + assert(s && u); + + return + u->latency + + (u->memchunk.memblock ? pa_bytes_to_usec(u->memchunk.length, &s->sample_spec) : 0); +} + +static void defer_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event*e, void *userdata) { + struct userdata *u = userdata; + assert(u); + do_work(u); +} + +static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { + struct userdata *u = userdata; + assert(u); + do_work(u); +} + +static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, void *userdata) { + struct userdata *u = userdata; + + pa_socket_client_unref(u->client); + u->client = NULL; + + if (!io) { + pa_log(__FILE__": connection failed: %s\n", strerror(errno)); + cancel(u); + return; + } + + u->io = io; + pa_iochannel_set_callback(u->io, io_callback, u); +} + +int pa__init(pa_core *c, pa_module*m) { + struct userdata *u = NULL; + const char *p; + pa_sample_spec ss; + pa_modargs *ma = NULL; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments\n"); + goto fail; + } + + ss = c->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &ss) < 0) { + pa_log(__FILE__": invalid sample format specification\n"); + goto fail; + } + + if ((ss.format != PA_SAMPLE_U8 && ss.format != PA_SAMPLE_S16NE) || + (ss.channels > 2)) { + pa_log(__FILE__": esound sample type support is limited to mono/stereo and U8 or S16NE sample data\n"); + goto fail; + } + + u = pa_xmalloc0(sizeof(struct userdata)); + u->core = c; + u->module = m; + m->userdata = u; + u->format = + (ss.format == PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16) | + (ss.channels == 2 ? ESD_STEREO : ESD_MONO); + u->rate = ss.rate; + u->sink = NULL; + u->client = NULL; + u->io = NULL; + u->read_data = u->write_data = NULL; + u->read_index = u->write_index = u->read_length = u->write_length = 0; + u->state = STATE_AUTH; + u->latency = 0; + + if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) { + pa_log(__FILE__": failed to create sink.\n"); + goto fail; + } + + if (!(u->client = pa_socket_client_new_string(u->core->mainloop, p = pa_modargs_get_value(ma, "server", ESD_UNIX_SOCKET_NAME), ESD_DEFAULT_PORT))) { + pa_log(__FILE__": failed to connect to server.\n"); + goto fail; + } + pa_socket_client_set_callback(u->client, on_connection, u); + + /* Prepare the initial request */ + u->write_data = pa_xmalloc(u->write_length = ESD_KEY_LEN + sizeof(int32_t)); + if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", ".esd_auth"), u->write_data, ESD_KEY_LEN) < 0) { + pa_log(__FILE__": failed to load cookie\n"); + goto fail; + } + *(int32_t*) ((uint8_t*) u->write_data + ESD_KEY_LEN) = ESD_ENDIAN_KEY; + + /* Reserve space for the response */ + u->read_data = pa_xmalloc(u->read_length = sizeof(int32_t)); + + u->sink->notify = notify_cb; + u->sink->get_latency = get_latency_cb; + u->sink->userdata = u; + pa_sink_set_owner(u->sink, m); + u->sink->description = pa_sprintf_malloc("Esound sink '%s'", p); + + u->memchunk.memblock = NULL; + u->memchunk.length = 0; + + u->defer_event = c->mainloop->defer_new(c->mainloop, defer_callback, u); + c->mainloop->defer_enable(u->defer_event, 0); + + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(c, m); + + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata *u; + assert(c && m); + + if (!(u = m->userdata)) + return; + + u->module = NULL; + cancel(u); + + if (u->memchunk.memblock) + pa_memblock_unref(u->memchunk.memblock); + + if (u->client) + pa_socket_client_unref(u->client); + + pa_xfree(u->read_data); + pa_xfree(u->write_data); + + pa_xfree(u); +} + + + diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c new file mode 100644 index 00000000..ea8a2bd2 --- /dev/null +++ b/src/modules/module-lirc.c @@ -0,0 +1,241 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "module-lirc-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("LIRC volume control") +PA_MODULE_VERSION(PACKAGE_VERSION) +PA_MODULE_USAGE("config= sink= appname=") + +static const char* const valid_modargs[] = { + "config", + "sink", + "appname", + NULL, +}; + +struct userdata { + int lirc_fd; + pa_io_event *io; + struct lirc_config *config; + char *sink_name; + pa_module *module; + float mute_toggle_save; +}; + +static int lirc_in_use = 0; + +static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void*userdata) { + struct userdata *u = userdata; + char *name = NULL, *code = NULL; + assert(io); + assert(u); + + if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) { + pa_log(__FILE__": lost connection to LIRC daemon.\n"); + goto fail; + } + + if (events & PA_IO_EVENT_INPUT) { + char *c; + + if (lirc_nextcode(&code) != 0 || !code) { + pa_log(__FILE__": lirc_nextcode() failed.\n"); + goto fail; + } + + c = pa_xstrdup(code); + c[strcspn(c, "\n\r")] = 0; + pa_log_debug(__FILE__": raw IR code '%s'\n", c); + pa_xfree(c); + + while (lirc_code2char(u->config, code, &name) == 0 && name) { + enum { INVALID, UP, DOWN, MUTE, RESET, MUTE_TOGGLE } volchange = INVALID; + + pa_log_info(__FILE__": translated IR code '%s'\n", name); + + if (strcasecmp(name, "volume-up") == 0) + volchange = UP; + else if (strcasecmp(name, "volume-down") == 0) + volchange = DOWN; + else if (strcasecmp(name, "mute") == 0) + volchange = MUTE; + else if (strcasecmp(name, "mute-toggle") == 0) + volchange = MUTE_TOGGLE; + else if (strcasecmp(name, "reset") == 0) + volchange = RESET; + + if (volchange == INVALID) + pa_log_warn(__FILE__": recieved unknown IR code '%s'\n", name); + else { + pa_sink *s; + + if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1))) + pa_log(__FILE__": failed to get sink '%s'\n", u->sink_name); + else { + pa_volume_t v = pa_cvolume_avg(pa_sink_get_volume(s, PA_MIXER_HARDWARE)); + pa_cvolume cv; +#define DELTA (PA_VOLUME_NORM/20) + + switch (volchange) { + case UP: + v += PA_VOLUME_NORM/20; + break; + + case DOWN: + if (v > DELTA) + v -= DELTA; + else + v = PA_VOLUME_MUTED; + + break; + + case MUTE: + v = PA_VOLUME_MUTED; + break; + + case RESET: + v = PA_VOLUME_NORM; + break; + + case MUTE_TOGGLE: { + + if (v > 0) { + u->mute_toggle_save = v; + v = PA_VOLUME_MUTED; + } else + v = u->mute_toggle_save; + } + default: + ; + } + + pa_cvolume_set(&cv, PA_CHANNELS_MAX, v); + pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv); + } + } + } + } + + free(code); + + return; + +fail: + u->module->core->mainloop->io_free(u->io); + u->io = NULL; + + pa_module_unload_request(u->module); + + free(code); +} + +int pa__init(pa_core *c, pa_module*m) { + pa_modargs *ma = NULL; + struct userdata *u; + assert(c && m); + + if (lirc_in_use) { + pa_log(__FILE__": module-lirc may no be loaded twice.\n"); + return -1; + } + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": Failed to parse module arguments\n"); + goto fail; + } + + m->userdata = u = pa_xmalloc(sizeof(struct userdata)); + u->module = m; + u->io = NULL; + u->config = NULL; + u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); + u->lirc_fd = -1; + u->mute_toggle_save = 0; + + if ((u->lirc_fd = lirc_init((char*) pa_modargs_get_value(ma, "appname", "polypaudio"), 1)) < 0) { + pa_log(__FILE__": lirc_init() failed.\n"); + goto fail; + } + + if (lirc_readconfig((char*) pa_modargs_get_value(ma, "config", NULL), &u->config, NULL) < 0) { + pa_log(__FILE__": lirc_readconfig() failed.\n"); + goto fail; + } + + u->io = c->mainloop->io_new(c->mainloop, u->lirc_fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u); + + lirc_in_use = 1; + + pa_modargs_free(ma); + + return 0; + +fail: + + if (ma) + pa_modargs_free(ma); + + pa__done(c, m); + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata *u; + assert(c); + assert(m); + + if (!(u = m->userdata)) + return; + + if (u->io) + m->core->mainloop->io_free(u->io); + + if (u->config) + lirc_freeconfig(u->config); + + if (u->lirc_fd >= 0) + lirc_deinit(); + + pa_xfree(u->sink_name); + pa_xfree(u); + + lirc_in_use = 0; +} diff --git a/src/modules/module-match.c b/src/modules/module-match.c new file mode 100644 index 00000000..10ceb75e --- /dev/null +++ b/src/modules/module-match.c @@ -0,0 +1,235 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "module-match-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("Sink input matching module") +PA_MODULE_USAGE("table=") +PA_MODULE_VERSION(PACKAGE_VERSION) + +#define WHITESPACE "\n\r \t" + +#ifndef DEFAULT_CONFIG_DIR +#define DEFAULT_CONFIG_DIR "/etc/polypaudio" +#endif + +#define DEFAULT_MATCH_TABLE_FILE DEFAULT_CONFIG_DIR"/match.table" +#define DEFAULT_MATCH_TABLE_FILE_USER ".polypaudio/match.table" + +static const char* const valid_modargs[] = { + "table", + NULL, +}; + +struct rule { + regex_t regex; + pa_volume_t volume; + struct rule *next; +}; + +struct userdata { + struct rule *rules; + pa_subscription *subscription; +}; + +static int load_rules(struct userdata *u, const char *filename) { + FILE *f; + int n = 0; + int ret = -1; + struct rule *end = NULL; + char *fn = NULL; + + f = filename ? + fopen(fn = pa_xstrdup(filename), "r") : + pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn); + + if (!f) { + pa_log(__FILE__": failed to open file '%s': %s\n", fn, strerror(errno)); + goto finish; + } + + while (!feof(f)) { + char *d, *v; + pa_volume_t volume; + uint32_t k; + regex_t regex; + char ln[256]; + struct rule *rule; + + if (!fgets(ln, sizeof(ln), f)) + break; + + n++; + + pa_strip_nl(ln); + + if (ln[0] == '#' || !*ln ) + continue; + + d = ln+strcspn(ln, WHITESPACE); + v = d+strspn(d, WHITESPACE); + + + if (!*v) { + pa_log(__FILE__ ": [%s:%u] failed to parse line - too few words\n", filename, n); + goto finish; + } + + *d = 0; + if (pa_atou(v, &k) < 0) { + pa_log(__FILE__": [%s:%u] failed to parse volume\n", filename, n); + goto finish; + } + + volume = (pa_volume_t) k; + + + if (regcomp(®ex, ln, REG_EXTENDED|REG_NOSUB) != 0) { + pa_log(__FILE__": [%s:%u] invalid regular expression\n", filename, n); + goto finish; + } + + rule = pa_xmalloc(sizeof(struct rule)); + rule->regex = regex; + rule->volume = volume; + rule->next = NULL; + + if (end) + end->next = rule; + else + u->rules = rule; + end = rule; + + *d = 0; + } + + ret = 0; + +finish: + if (f) + fclose(f); + + if (fn) + pa_xfree(fn); + + return ret; +} + +static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + struct userdata *u = userdata; + pa_sink_input *si; + struct rule *r; + assert(c && u); + + if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW)) + return; + + if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx))) + return; + + if (!si->name) + return; + + for (r = u->rules; r; r = r->next) { + if (!regexec(&r->regex, si->name, 0, NULL, 0)) { + pa_cvolume cv; + pa_log_debug(__FILE__": changing volume of sink input '%s' to 0x%03x\n", si->name, r->volume); + pa_cvolume_set(&cv, r->volume, si->sample_spec.channels); + pa_sink_input_set_volume(si, &cv); + } + } +} + +int pa__init(pa_core *c, pa_module*m) { + pa_modargs *ma = NULL; + struct userdata *u; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": Failed to parse module arguments\n"); + goto fail; + } + + u = pa_xmalloc(sizeof(struct userdata)); + u->rules = NULL; + u->subscription = NULL; + m->userdata = u; + + if (load_rules(u, pa_modargs_get_value(ma, "table", NULL)) < 0) + goto fail; + + u->subscription = pa_subscription_new(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, callback, u); + + pa_modargs_free(ma); + return 0; + +fail: + pa__done(c, m); + + if (ma) + pa_modargs_free(ma); + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata* u; + struct rule *r, *n; + assert(c && m); + + if (!(u = m->userdata)) + return; + + if (u->subscription) + pa_subscription_free(u->subscription); + + for (r = u->rules; r; r = n) { + n = r->next; + + regfree(&r->regex); + pa_xfree(r); + } + + pa_xfree(u); +} + + diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c new file mode 100644 index 00000000..b60f786d --- /dev/null +++ b/src/modules/module-mmkbd-evdev.c @@ -0,0 +1,250 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "module-mmkbd-evdev-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("Multimedia keyboard support via Linux evdev") +PA_MODULE_VERSION(PACKAGE_VERSION) +PA_MODULE_USAGE("device= sink=") + +#define DEFAULT_DEVICE "/dev/input/event0" + +/* + * This isn't defined in older kernel headers and there is no way of + * detecting it. + */ +struct _input_id { + __u16 bustype; + __u16 vendor; + __u16 product; + __u16 version; +}; + +static const char* const valid_modargs[] = { + "device", + "sink", + NULL, +}; + +struct userdata { + int fd; + pa_io_event *io; + char *sink_name; + pa_module *module; + float mute_toggle_save; +}; + +static void io_callback(pa_mainloop_api *io, PA_GCC_UNUSED pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t events, void*userdata) { + struct userdata *u = userdata; + assert(io); + assert(u); + + if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) { + pa_log(__FILE__": lost connection to evdev device.\n"); + goto fail; + } + + if (events & PA_IO_EVENT_INPUT) { + struct input_event ev; + + if (pa_loop_read(u->fd, &ev, sizeof(ev)) <= 0) { + pa_log(__FILE__": failed to read from event device: %s\n", strerror(errno)); + goto fail; + } + + if (ev.type == EV_KEY && (ev.value == 1 || ev.value == 2)) { + enum { INVALID, UP, DOWN, MUTE_TOGGLE } volchange = INVALID; + + pa_log_debug(__FILE__": key code=%u, value=%u\n", ev.code, ev.value); + + switch (ev.code) { + case KEY_VOLUMEDOWN: volchange = DOWN; break; + case KEY_VOLUMEUP: volchange = UP; break; + case KEY_MUTE: volchange = MUTE_TOGGLE; break; + } + + if (volchange != INVALID) { + pa_sink *s; + + if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1))) + pa_log(__FILE__": failed to get sink '%s'\n", u->sink_name); + else { + pa_volume_t v = pa_cvolume_avg(pa_sink_get_volume(s, PA_MIXER_HARDWARE)); + pa_cvolume cv; +#define DELTA (PA_VOLUME_NORM/20) + + switch (volchange) { + case UP: + v += DELTA; + break; + + case DOWN: + if (v > DELTA) + v -= DELTA; + else + v = PA_VOLUME_MUTED; + + break; + + case MUTE_TOGGLE: { + + if (v > 0) { + u->mute_toggle_save = v; + v = PA_VOLUME_MUTED; + } else + v = u->mute_toggle_save; + } + default: + ; + } + + pa_cvolume_set(&cv, PA_CHANNELS_MAX, v); + pa_sink_set_volume(s, PA_MIXER_HARDWARE, &cv); + } + } + } + } + + return; + +fail: + u->module->core->mainloop->io_free(u->io); + u->io = NULL; + + pa_module_unload_request(u->module); +} + +#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8))) + +int pa__init(pa_core *c, pa_module*m) { + pa_modargs *ma = NULL; + struct userdata *u; + int version; + struct _input_id input_id; + char name[256]; + uint8_t evtype_bitmask[EV_MAX/8 + 1]; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": Failed to parse module arguments\n"); + goto fail; + } + + m->userdata = u = pa_xmalloc(sizeof(struct userdata)); + u->module = m; + u->io = NULL; + u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); + u->fd = -1; + u->mute_toggle_save = 0; + + if ((u->fd = open(pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), O_RDONLY)) < 0) { + pa_log(__FILE__": failed to open evdev device: %s\n", strerror(errno)); + goto fail; + } + + if (ioctl(u->fd, EVIOCGVERSION, &version) < 0) { + pa_log(__FILE__": EVIOCGVERSION failed: %s\n", strerror(errno)); + goto fail; + } + + pa_log_info(__FILE__": evdev driver version %i.%i.%i\n", version >> 16, (version >> 8) & 0xff, version & 0xff); + + if(ioctl(u->fd, EVIOCGID, &input_id)) { + pa_log(__FILE__": EVIOCGID failed: %s\n", strerror(errno)); + goto fail; + } + + pa_log_info(__FILE__": evdev vendor 0x%04hx product 0x%04hx version 0x%04hx bustype %u\n", + input_id.vendor, input_id.product, input_id.version, input_id.bustype); + + if(ioctl(u->fd, EVIOCGNAME(sizeof(name)), name) < 0) { + pa_log(__FILE__": EVIOCGNAME failed: %s\n", strerror(errno)); + goto fail; + } + + pa_log_info(__FILE__": evdev device name: %s\n", name); + + memset(evtype_bitmask, 0, sizeof(evtype_bitmask)); + if (ioctl(u->fd, EVIOCGBIT(0, EV_MAX), evtype_bitmask) < 0) { + pa_log(__FILE__": EVIOCGBIT failed: %s\n", strerror(errno)); + goto fail; + } + + if (!test_bit(EV_KEY, evtype_bitmask)) { + pa_log(__FILE__": device has no keys.\n"); + goto fail; + } + + u->io = c->mainloop->io_new(c->mainloop, u->fd, PA_IO_EVENT_INPUT|PA_IO_EVENT_HANGUP, io_callback, u); + + pa_modargs_free(ma); + + return 0; + +fail: + + if (ma) + pa_modargs_free(ma); + + pa__done(c, m); + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata *u; + assert(c); + assert(m); + + if (!(u = m->userdata)) + return; + + if (u->io) + m->core->mainloop->io_free(u->io); + + if (u->fd >= 0) + close(u->fd); + + pa_xfree(u->sink_name); + pa_xfree(u); +} diff --git a/src/modules/module-native-protocol-fd.c b/src/modules/module-native-protocol-fd.c new file mode 100644 index 00000000..abc531b3 --- /dev/null +++ b/src/modules/module-native-protocol-fd.c @@ -0,0 +1,85 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "module-native-protocol-fd-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("Native protocol autospawn helper") +PA_MODULE_VERSION(PACKAGE_VERSION) + +static const char* const valid_modargs[] = { + "fd", + "public", + "cookie", + NULL, +}; + +int pa__init(pa_core *c, pa_module*m) { + pa_iochannel *io; + pa_modargs *ma; + int fd, r = -1; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments.\n"); + goto finish; + } + + if (pa_modargs_get_value_s32(ma, "fd", &fd) < 0) { + pa_log(__FILE__": invalid file descriptor.\n"); + goto finish; + } + + io = pa_iochannel_new(c->mainloop, fd, fd); + + if (!(m->userdata = pa_protocol_native_new_iochannel(c, io, m, ma))) { + pa_iochannel_free(io); + goto finish; + } + + r = 0; + +finish: + if (ma) + pa_modargs_free(ma); + + return r; +} + +void pa__done(pa_core *c, pa_module*m) { + assert(c && m); + + pa_protocol_native_free(m->userdata); +} diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c new file mode 100644 index 00000000..5731a403 --- /dev/null +++ b/src/modules/module-null-sink.c @@ -0,0 +1,150 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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= channels= rate= sink_name=") + +#define DEFAULT_SINK_NAME "null" + +struct userdata { + pa_core *core; + pa_module *module; + pa_sink *sink; + pa_time_event *time_event; + size_t block_size; +}; + +static const char* const valid_modargs[] = { + "rate", + "format", + "channels", + "sink_name", + NULL +}; + +static void time_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) { + struct userdata *u = userdata; + pa_memchunk chunk; + struct timeval ntv = *tv; + size_t l; + + assert(u); + + if (pa_sink_render(u->sink, u->block_size, &chunk) >= 0) { + l = chunk.length; + pa_memblock_unref(chunk.memblock); + } else + l = u->block_size; + + pa_timeval_add(&ntv, pa_bytes_to_usec(l, &u->sink->sample_spec)); + m->time_restart(e, &ntv); +} + +int pa__init(pa_core *c, pa_module*m) { + struct userdata *u = NULL; + pa_sample_spec ss; + pa_modargs *ma = NULL; + struct timeval tv; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments.\n"); + goto fail; + } + + ss = c->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &ss) < 0) { + pa_log(__FILE__": invalid sample format specification.\n"); + goto fail; + } + + u = pa_xmalloc0(sizeof(struct userdata)); + u->core = c; + u->module = m; + m->userdata = u; + + if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) { + pa_log(__FILE__": failed to create sink.\n"); + goto fail; + } + + u->sink->userdata = u; + pa_sink_set_owner(u->sink, m); + u->sink->description = pa_sprintf_malloc("NULL sink"); + + pa_gettimeofday(&tv); + u->time_event = c->mainloop->time_new(c->mainloop, &tv, time_callback, u); + + u->block_size = pa_bytes_per_second(&ss) / 10; + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(c, m); + + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata *u; + assert(c && m); + + if (!(u = m->userdata)) + return; + + pa_sink_disconnect(u->sink); + pa_sink_unref(u->sink); + + u->core->mainloop->time_free(u->time_event); + + pa_xfree(u); +} diff --git a/src/modules/module-oss-mmap.c b/src/modules/module-oss-mmap.c new file mode 100644 index 00000000..6986b03c --- /dev/null +++ b/src/modules/module-oss-mmap.c @@ -0,0 +1,426 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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= source_name= device= record= playback= format= channels= rate= fragments= fragment_size=") + +struct userdata { + pa_sink *sink; + pa_source *source; + pa_core *core; + pa_sample_spec sample_spec; + + size_t in_fragment_size, out_fragment_size, in_fragments, out_fragments, out_fill; + + int fd; + + void *in_mmap, *out_mmap; + size_t in_mmap_length, out_mmap_length; + + pa_io_event *io_event; + + pa_memblock **in_memblocks, **out_memblocks; + unsigned out_current, in_current; + pa_module *module; +}; + +static const char* const valid_modargs[] = { + "sink_name", + "source_name", + "device", + "record", + "playback", + "fragments", + "fragment_size", + "format", + "rate", + "channels", + NULL +}; + +#define DEFAULT_SINK_NAME "oss_output" +#define DEFAULT_SOURCE_NAME "oss_input" +#define DEFAULT_DEVICE "/dev/dsp" + +static void update_usage(struct userdata *u) { + pa_module_set_used(u->module, + (u->sink ? pa_idxset_size(u->sink->inputs) : 0) + + (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0) + + (u->source ? pa_idxset_size(u->source->outputs) : 0)); +} + +static void out_fill_memblocks(struct userdata *u, unsigned n) { + assert(u && u->out_memblocks); + + while (n > 0) { + pa_memchunk chunk; + + if (u->out_memblocks[u->out_current]) + pa_memblock_unref_fixed(u->out_memblocks[u->out_current]); + + chunk.memblock = u->out_memblocks[u->out_current] = pa_memblock_new_fixed((uint8_t*)u->out_mmap+u->out_fragment_size*u->out_current, u->out_fragment_size, 1, u->core->memblock_stat); + assert(chunk.memblock); + chunk.length = chunk.memblock->length; + chunk.index = 0; + + pa_sink_render_into_full(u->sink, &chunk); + + u->out_current++; + while (u->out_current >= u->out_fragments) + u->out_current -= u->out_fragments; + + n--; + } +} + +static void do_write(struct userdata *u) { + struct count_info info; + assert(u && u->sink); + + update_usage(u); + + if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) { + pa_log(__FILE__": SNDCTL_DSP_GETOPTR: %s\n", strerror(errno)); + return; + } + + u->out_fill = (u->out_fragment_size * u->out_fragments) - (info.ptr % u->out_fragment_size); + + if (!info.blocks) + return; + + out_fill_memblocks(u, info.blocks); +} + +static void in_post_memblocks(struct userdata *u, unsigned n) { + assert(u && u->in_memblocks); + + while (n > 0) { + pa_memchunk chunk; + + if (!u->in_memblocks[u->in_current]) { + chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed((uint8_t*) u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size, 1, u->core->memblock_stat); + chunk.length = chunk.memblock->length; + chunk.index = 0; + + pa_source_post(u->source, &chunk); + } + + u->in_current++; + while (u->in_current >= u->in_fragments) + u->in_current -= u->in_fragments; + + n--; + } +} + +static void in_clear_memblocks(struct userdata*u, unsigned n) { + unsigned i = u->in_current; + assert(u && u->in_memblocks); + + if (n > u->in_fragments) + n = u->in_fragments; + + while (n > 0) { + if (u->in_memblocks[i]) { + pa_memblock_unref_fixed(u->in_memblocks[i]); + u->in_memblocks[i] = NULL; + } + + i++; + while (i >= u->in_fragments) + i -= u->in_fragments; + + n--; + } +} + +static void do_read(struct userdata *u) { + struct count_info info; + assert(u && u->source); + + update_usage(u); + + if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) { + pa_log(__FILE__": SNDCTL_DSP_GETIPTR: %s\n", strerror(errno)); + return; + } + + if (!info.blocks) + return; + + in_post_memblocks(u, info.blocks); + in_clear_memblocks(u, u->in_fragments/2); +} + +static void io_callback(pa_mainloop_api *m, pa_io_event *e, PA_GCC_UNUSED int fd, pa_io_event_flags_t f, void *userdata) { + struct userdata *u = userdata; + assert (u && u->core->mainloop == m && u->io_event == e); + + if (f & PA_IO_EVENT_INPUT) + do_read(u); + if (f & PA_IO_EVENT_OUTPUT) + do_write(u); +} + +static pa_usec_t sink_get_latency_cb(pa_sink *s) { + struct userdata *u = s->userdata; + assert(s && u); + + do_write(u); + return pa_bytes_to_usec(u->out_fill, &s->sample_spec); +} + +int pa__init(pa_core *c, pa_module*m) { + struct audio_buf_info info; + struct userdata *u = NULL; + const char *p; + int nfrags, frag_size; + int mode, caps; + int enable_bits = 0, zero = 0; + int playback = 1, record = 1; + pa_modargs *ma = NULL; + assert(c && m); + + m->userdata = u = pa_xmalloc0(sizeof(struct userdata)); + u->module = m; + u->fd = -1; + u->core = c; + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments.\n"); + goto fail; + } + + if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) { + pa_log(__FILE__": record= and playback= expect numeric arguments.\n"); + goto fail; + } + + if (!playback && !record) { + pa_log(__FILE__": neither playback nor record enabled for device.\n"); + goto fail; + } + + mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0)); + + nfrags = 12; + frag_size = 1024; + if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) { + pa_log(__FILE__": failed to parse fragments arguments\n"); + goto fail; + } + + u->sample_spec = c->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &u->sample_spec) < 0) { + pa_log(__FILE__": failed to parse sample specification\n"); + goto fail; + } + + if ((u->fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0) + goto fail; + + if (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_REALTIME) || !(caps & DSP_CAP_TRIGGER)) { + pa_log(__FILE__": OSS device not mmap capable.\n"); + goto fail; + } + + pa_log(__FILE__": device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR")); + + if (nfrags >= 2 && frag_size >= 1) + if (pa_oss_set_fragments(u->fd, nfrags, frag_size) < 0) + goto fail; + + if (pa_oss_auto_format(u->fd, &u->sample_spec) < 0) + goto fail; + + if (mode != O_WRONLY) { + if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) { + pa_log(__FILE__": SNDCTL_DSP_GETISPACE: %s\n", strerror(errno)); + goto fail; + } + + pa_log_info(__FILE__": input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); + u->in_mmap_length = (u->in_fragment_size = info.fragsize) * (u->in_fragments = info.fragstotal); + + if ((u->in_mmap = mmap(NULL, u->in_mmap_length, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) { + if (mode == O_RDWR) { + pa_log(__FILE__": mmap failed for input. Changing to O_WRONLY mode.\n"); + mode = O_WRONLY; + } else { + pa_log(__FILE__": mmap(): %s\n", strerror(errno)); + goto fail; + } + } else { + + u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &u->sample_spec, NULL); + assert(u->source); + u->source->userdata = u; + pa_source_set_owner(u->source, m); + u->source->description = pa_sprintf_malloc("Open Sound System PCM/mmap() on '%s'", p); + + u->in_memblocks = pa_xmalloc0(sizeof(pa_memblock *)*u->in_fragments); + + enable_bits |= PCM_ENABLE_INPUT; + } + } + + if (mode != O_RDONLY) { + if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { + pa_log(__FILE__": SNDCTL_DSP_GETOSPACE: %s\n", strerror(errno)); + goto fail; + } + + pa_log_info(__FILE__": output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); + u->out_mmap_length = (u->out_fragment_size = info.fragsize) * (u->out_fragments = info.fragstotal); + + if ((u->out_mmap = mmap(NULL, u->out_mmap_length, PROT_WRITE, MAP_SHARED, u->fd, 0)) == MAP_FAILED) { + if (mode == O_RDWR) { + pa_log(__FILE__": mmap filed for output. Changing to O_RDONLY mode.\n"); + mode = O_RDONLY; + } else { + pa_log(__FILE__": mmap(): %s\n", strerror(errno)); + goto fail; + } + } else { + pa_silence_memory(u->out_mmap, u->out_mmap_length, &u->sample_spec); + + u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &u->sample_spec, NULL); + assert(u->sink); + u->sink->get_latency = sink_get_latency_cb; + u->sink->userdata = u; + pa_sink_set_owner(u->sink, m); + u->sink->description = pa_sprintf_malloc("Open Sound System PCM/mmap() on '%s'", p); + + u->out_memblocks = pa_xmalloc0(sizeof(struct memblock *)*u->out_fragments); + + enable_bits |= PCM_ENABLE_OUTPUT; + } + } + + zero = 0; + if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero) < 0) { + pa_log(__FILE__": SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno)); + goto fail; + } + + if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) { + pa_log(__FILE__": SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno)); + goto fail; + } + + assert(u->source || u->sink); + + u->io_event = c->mainloop->io_new(c->mainloop, u->fd, (u->source ? PA_IO_EVENT_INPUT : 0) | (u->sink ? PA_IO_EVENT_OUTPUT : 0), io_callback, u); + assert(u->io_event); + + pa_modargs_free(ma); + + return 0; + +fail: + pa__done(c, m); + + if (ma) + pa_modargs_free(ma); + + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata *u; + assert(c && m); + + if (!(u = m->userdata)) + return; + + if (u->out_memblocks) { + unsigned i; + for (i = 0; i < u->out_fragments; i++) + if (u->out_memblocks[i]) + pa_memblock_unref_fixed(u->out_memblocks[i]); + pa_xfree(u->out_memblocks); + } + + if (u->in_memblocks) { + unsigned i; + for (i = 0; i < u->in_fragments; i++) + if (u->in_memblocks[i]) + pa_memblock_unref_fixed(u->in_memblocks[i]); + pa_xfree(u->in_memblocks); + } + + if (u->in_mmap && u->in_mmap != MAP_FAILED) + munmap(u->in_mmap, u->in_mmap_length); + + if (u->out_mmap && u->out_mmap != MAP_FAILED) + munmap(u->out_mmap, u->out_mmap_length); + + if (u->sink) { + pa_sink_disconnect(u->sink); + pa_sink_unref(u->sink); + } + + if (u->source) { + pa_source_disconnect(u->source); + pa_source_unref(u->source); + } + + if (u->io_event) + u->core->mainloop->io_free(u->io_event); + + if (u->fd >= 0) + close(u->fd); + + pa_xfree(u); +} diff --git a/src/modules/module-oss.c b/src/modules/module-oss.c new file mode 100644 index 00000000..04458419 --- /dev/null +++ b/src/modules/module-oss.c @@ -0,0 +1,468 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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= source_name= device= record= playback= format= channels= rate= fragments= fragment_size=") + +struct userdata { + pa_sink *sink; + pa_source *source; + pa_iochannel *io; + pa_core *core; + + pa_memchunk memchunk, silence; + + uint32_t in_fragment_size, out_fragment_size, sample_size; + int use_getospace, use_getispace; + + int fd; + pa_module *module; +}; + +static const char* const valid_modargs[] = { + "sink_name", + "source_name", + "device", + "record", + "playback", + "fragments", + "fragment_size", + "format", + "rate", + "channels", + NULL +}; + +#define DEFAULT_SINK_NAME "oss_output" +#define DEFAULT_SOURCE_NAME "oss_input" +#define DEFAULT_DEVICE "/dev/dsp" + +static void update_usage(struct userdata *u) { + pa_module_set_used(u->module, + (u->sink ? pa_idxset_size(u->sink->inputs) : 0) + + (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0) + + (u->source ? pa_idxset_size(u->source->outputs) : 0)); +} + +static void do_write(struct userdata *u) { + pa_memchunk *memchunk; + ssize_t r; + size_t l; + int loop = 0; + + assert(u); + + if (!u->sink || !pa_iochannel_is_writable(u->io)) + return; + + update_usage(u); + + l = u->out_fragment_size; + + if (u->use_getospace) { + audio_buf_info info; + + if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) + u->use_getospace = 0; + else { + if (info.bytes/l > 0) { + l = (info.bytes/l)*l; + loop = 1; + } + } + } + + do { + memchunk = &u->memchunk; + + if (!memchunk->length) + if (pa_sink_render(u->sink, l, memchunk) < 0) + memchunk = &u->silence; + + assert(memchunk->memblock); + assert(memchunk->memblock->data); + assert(memchunk->length); + + if ((r = pa_iochannel_write(u->io, (uint8_t*) memchunk->memblock->data + memchunk->index, memchunk->length)) < 0) { + pa_log(__FILE__": write() failed: %s\n", strerror(errno)); + break; + } + + if (memchunk == &u->silence) + assert(r % u->sample_size == 0); + else { + u->memchunk.index += r; + u->memchunk.length -= r; + + if (u->memchunk.length <= 0) { + pa_memblock_unref(u->memchunk.memblock); + u->memchunk.memblock = NULL; + } + } + + l = l > (size_t) r ? l - r : 0; + } while (loop && l > 0); +} + +static void do_read(struct userdata *u) { + pa_memchunk memchunk; + ssize_t r; + size_t l; + int loop = 0; + assert(u); + + if (!u->source || !pa_iochannel_is_readable(u->io) || !pa_idxset_size(u->source->outputs)) + return; + + update_usage(u); + + l = u->in_fragment_size; + + if (u->use_getispace) { + audio_buf_info info; + + if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) + u->use_getispace = 0; + else { + if (info.bytes/l > 0) { + l = (info.bytes/l)*l; + loop = 1; + } + } + } + + do { + memchunk.memblock = pa_memblock_new(l, u->core->memblock_stat); + assert(memchunk.memblock); + if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) { + pa_memblock_unref(memchunk.memblock); + if (errno != EAGAIN) + pa_log(__FILE__": read() failed: %s\n", strerror(errno)); + break; + } + + assert(r <= (ssize_t) memchunk.memblock->length); + memchunk.length = memchunk.memblock->length = r; + memchunk.index = 0; + + pa_source_post(u->source, &memchunk); + pa_memblock_unref(memchunk.memblock); + + l = l > (size_t) r ? l - r : 0; + } while (loop && l > 0); +} + +static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { + struct userdata *u = userdata; + assert(u); + do_write(u); + do_read(u); +} + +static void source_notify_cb(pa_source *s) { + struct userdata *u = s->userdata; + assert(u); + do_read(u); +} + +static pa_usec_t sink_get_latency_cb(pa_sink *s) { + pa_usec_t r = 0; + int arg; + struct userdata *u = s->userdata; + assert(s && u && u->sink); + + if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) { + pa_log_info(__FILE__": device doesn't support SNDCTL_DSP_GETODELAY: %s\n", strerror(errno)); + s->get_latency = NULL; + return 0; + } + + r += pa_bytes_to_usec(arg, &s->sample_spec); + + if (u->memchunk.memblock) + r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec); + + return r; +} + +static pa_usec_t source_get_latency_cb(pa_source *s) { + struct userdata *u = s->userdata; + audio_buf_info info; + assert(s && u && u->source); + + if (!u->use_getispace) + return 0; + + if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) { + u->use_getispace = 0; + return 0; + } + + if (info.bytes <= 0) + return 0; + + return pa_bytes_to_usec(info.bytes, &s->sample_spec); +} + +static int sink_get_hw_volume(pa_sink *s) { + struct userdata *u = s->userdata; + char cv[PA_CVOLUME_SNPRINT_MAX]; + unsigned vol; + + if (ioctl(u->fd, SOUND_MIXER_READ_PCM, &vol) < 0) { + pa_log_info(__FILE__": device doesn't support reading mixer settings: %s\n", strerror(errno)); + s->get_hw_volume = NULL; + return -1; + } + + s->hw_volume.values[0] = ((vol & 0xFF) * PA_VOLUME_NORM) / 100; + + if ((s->hw_volume.channels = s->sample_spec.channels) >= 2) + s->hw_volume.values[1] = (((vol >> 8) & 0xFF) * PA_VOLUME_NORM) / 100; + + pa_log_info(__FILE__": Read mixer settings: %s\n", pa_cvolume_snprint(cv, sizeof(cv), &s->hw_volume)); + return 0; +} + +static int sink_set_hw_volume(pa_sink *s) { + struct userdata *u = s->userdata; + char cv[PA_CVOLUME_SNPRINT_MAX]; + unsigned vol; + + vol = (s->hw_volume.values[0]*100)/PA_VOLUME_NORM; + + if (s->sample_spec.channels >= 2) + vol |= ((s->hw_volume.values[1]*100)/PA_VOLUME_NORM) << 8; + + if (ioctl(u->fd, SOUND_MIXER_WRITE_PCM, &vol) < 0) { + pa_log_info(__FILE__": device doesn't support writing mixer settings: %s\n", strerror(errno)); + s->set_hw_volume = NULL; + return -1; + } + + pa_log_info(__FILE__": Wrote mixer settings: %s\n", pa_cvolume_snprint(cv, sizeof(cv), &s->hw_volume)); + return 0; +} + +int pa__init(pa_core *c, pa_module*m) { + struct audio_buf_info info; + struct userdata *u = NULL; + const char *p; + int fd = -1; + int nfrags, frag_size, in_frag_size, out_frag_size; + int mode; + int record = 1, playback = 1; + pa_sample_spec ss; + pa_modargs *ma = NULL; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments.\n"); + goto fail; + } + + if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) { + pa_log(__FILE__": record= and playback= expect numeric argument.\n"); + goto fail; + } + + if (!playback && !record) { + pa_log(__FILE__": neither playback nor record enabled for device.\n"); + goto fail; + } + + mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0)); + + nfrags = 12; + frag_size = 1024; + if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) { + pa_log(__FILE__": failed to parse fragments arguments\n"); + goto fail; + } + + ss = c->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &ss) < 0) { + pa_log(__FILE__": failed to parse sample specification\n"); + goto fail; + } + + if ((fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, NULL)) < 0) + goto fail; + + pa_log_info(__FILE__": device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR")); + + if (nfrags >= 2 && frag_size >= 1) + if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0) + goto fail; + + if (pa_oss_auto_format(fd, &ss) < 0) + goto fail; + + if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) { + pa_log(__FILE__": SNDCTL_DSP_GETBLKSIZE: %s\n", strerror(errno)); + goto fail; + } + assert(frag_size); + in_frag_size = out_frag_size = frag_size; + + u = pa_xmalloc(sizeof(struct userdata)); + u->core = c; + u->use_getospace = u->use_getispace = 0; + + if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) { + pa_log_info(__FILE__": input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); + in_frag_size = info.fragsize; + u->use_getispace = 1; + } + + if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) { + pa_log_info(__FILE__": output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); + out_frag_size = info.fragsize; + u->use_getospace = 1; + } + + if (mode != O_WRONLY) { + u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL); + assert(u->source); + u->source->userdata = u; + u->source->notify = source_notify_cb; + u->source->get_latency = source_get_latency_cb; + pa_source_set_owner(u->source, m); + u->source->description = pa_sprintf_malloc("Open Sound System PCM on '%s'", p); + } else + u->source = NULL; + + if (mode != O_RDONLY) { + u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL); + assert(u->sink); + u->sink->get_latency = sink_get_latency_cb; + u->sink->get_hw_volume = sink_get_hw_volume; + u->sink->set_hw_volume = sink_set_hw_volume; + u->sink->userdata = u; + pa_sink_set_owner(u->sink, m); + u->sink->description = pa_sprintf_malloc("Open Sound System PCM on '%s'", p); + } else + u->sink = NULL; + + assert(u->source || u->sink); + + u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : -1); + assert(u->io); + pa_iochannel_set_callback(u->io, io_callback, u); + u->fd = fd; + + u->memchunk.memblock = NULL; + u->memchunk.length = 0; + u->sample_size = pa_frame_size(&ss); + + u->out_fragment_size = out_frag_size; + u->in_fragment_size = in_frag_size; + u->silence.memblock = pa_memblock_new(u->silence.length = u->out_fragment_size, u->core->memblock_stat); + assert(u->silence.memblock); + pa_silence_memblock(u->silence.memblock, &ss); + u->silence.index = 0; + + u->module = m; + m->userdata = u; + + pa_modargs_free(ma); + + /* + * Some crappy drivers do not start the recording until we read something. + * Without this snippet, poll will never register the fd as ready. + */ + if (u->source) { + char buf[u->sample_size]; + read(u->fd, buf, u->sample_size); + } + + /* Read mixer settings */ + if (u->sink) + sink_get_hw_volume(u->sink); + + return 0; + +fail: + if (fd >= 0) + close(fd); + + if (ma) + pa_modargs_free(ma); + + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata *u; + assert(c && m); + + if (!(u = m->userdata)) + return; + + if (u->memchunk.memblock) + pa_memblock_unref(u->memchunk.memblock); + if (u->silence.memblock) + pa_memblock_unref(u->silence.memblock); + + if (u->sink) { + pa_sink_disconnect(u->sink); + pa_sink_unref(u->sink); + } + + if (u->source) { + pa_source_disconnect(u->source); + pa_source_unref(u->source); + } + + pa_iochannel_free(u->io); + pa_xfree(u); +} diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c new file mode 100644 index 00000000..6ace377f --- /dev/null +++ b/src/modules/module-pipe-sink.c @@ -0,0 +1,237 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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= file= format= channels= rate=") + +#define DEFAULT_FIFO_NAME "/tmp/music.output" +#define DEFAULT_SINK_NAME "fifo_output" + +struct userdata { + pa_core *core; + + char *filename; + + pa_sink *sink; + pa_iochannel *io; + pa_defer_event *defer_event; + + pa_memchunk memchunk; + pa_module *module; +}; + +static const char* const valid_modargs[] = { + "file", + "rate", + "format", + "channels", + "sink_name", + NULL +}; + +static void do_write(struct userdata *u) { + ssize_t r; + assert(u); + + u->core->mainloop->defer_enable(u->defer_event, 0); + + if (!pa_iochannel_is_writable(u->io)) + return; + + pa_module_set_used(u->module, pa_idxset_size(u->sink->inputs) + pa_idxset_size(u->sink->monitor_source->outputs)); + + if (!u->memchunk.length) + if (pa_sink_render(u->sink, PIPE_BUF, &u->memchunk) < 0) + return; + + assert(u->memchunk.memblock && u->memchunk.length); + + if ((r = pa_iochannel_write(u->io, (uint8_t*) u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) { + pa_log(__FILE__": write() failed: %s\n", strerror(errno)); + return; + } + + u->memchunk.index += r; + u->memchunk.length -= r; + + if (u->memchunk.length <= 0) { + pa_memblock_unref(u->memchunk.memblock); + u->memchunk.memblock = NULL; + } +} + +static void notify_cb(pa_sink*s) { + struct userdata *u = s->userdata; + assert(s && u); + + if (pa_iochannel_is_writable(u->io)) + u->core->mainloop->defer_enable(u->defer_event, 1); +} + +static pa_usec_t get_latency_cb(pa_sink *s) { + struct userdata *u = s->userdata; + assert(s && u); + + return u->memchunk.memblock ? pa_bytes_to_usec(u->memchunk.length, &s->sample_spec) : 0; +} + +static void defer_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event*e, void *userdata) { + struct userdata *u = userdata; + assert(u); + do_write(u); +} + +static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { + struct userdata *u = userdata; + assert(u); + do_write(u); +} + +int pa__init(pa_core *c, pa_module*m) { + struct userdata *u = NULL; + struct stat st; + const char *p; + int fd = -1; + pa_sample_spec ss; + pa_modargs *ma = NULL; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments\n"); + goto fail; + } + + ss = c->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &ss) < 0) { + pa_log(__FILE__": invalid sample format specification\n"); + goto fail; + } + + mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777); + + if ((fd = open(p, O_RDWR)) < 0) { + pa_log(__FILE__": open('%s'): %s\n", p, strerror(errno)); + goto fail; + } + + pa_fd_set_cloexec(fd, 1); + + if (fstat(fd, &st) < 0) { + pa_log(__FILE__": fstat('%s'): %s\n", p, strerror(errno)); + goto fail; + } + + if (!S_ISFIFO(st.st_mode)) { + pa_log(__FILE__": '%s' is not a FIFO.\n", p); + goto fail; + } + + u = pa_xmalloc0(sizeof(struct userdata)); + u->filename = pa_xstrdup(p); + u->core = c; + u->module = m; + m->userdata = u; + + if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) { + pa_log(__FILE__": failed to create sink.\n"); + goto fail; + } + u->sink->notify = notify_cb; + u->sink->get_latency = get_latency_cb; + u->sink->userdata = u; + pa_sink_set_owner(u->sink, m); + u->sink->description = pa_sprintf_malloc("Unix FIFO sink '%s'", p); + assert(u->sink->description); + + u->io = pa_iochannel_new(c->mainloop, -1, fd); + assert(u->io); + pa_iochannel_set_callback(u->io, io_callback, u); + + u->memchunk.memblock = NULL; + u->memchunk.length = 0; + + u->defer_event = c->mainloop->defer_new(c->mainloop, defer_callback, u); + assert(u->defer_event); + c->mainloop->defer_enable(u->defer_event, 0); + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + if (fd >= 0) + close(fd); + + pa__done(c, m); + + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata *u; + assert(c && m); + + if (!(u = m->userdata)) + return; + + if (u->memchunk.memblock) + pa_memblock_unref(u->memchunk.memblock); + + pa_sink_disconnect(u->sink); + pa_sink_unref(u->sink); + pa_iochannel_free(u->io); + u->core->mainloop->defer_free(u->defer_event); + + assert(u->filename); + unlink(u->filename); + pa_xfree(u->filename); + + pa_xfree(u); +} diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c new file mode 100644 index 00000000..a7bb0ce7 --- /dev/null +++ b/src/modules/module-pipe-source.c @@ -0,0 +1,210 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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= file= format= channels= rate=") + +#define DEFAULT_FIFO_NAME "/tmp/music.input" +#define DEFAULT_SOURCE_NAME "fifo_input" + +struct userdata { + pa_core *core; + + char *filename; + + pa_source *source; + pa_iochannel *io; + pa_module *module; + pa_memchunk chunk; +}; + +static const char* const valid_modargs[] = { + "file", + "rate", + "channels", + "format", + "source_name", + NULL +}; + +static void do_read(struct userdata *u) { + ssize_t r; + pa_memchunk chunk; + assert(u); + + if (!pa_iochannel_is_readable(u->io)) + return; + + pa_module_set_used(u->module, pa_idxset_size(u->source->outputs)); + + if (!u->chunk.memblock) { + u->chunk.memblock = pa_memblock_new(1024, u->core->memblock_stat); + u->chunk.index = chunk.length = 0; + } + + assert(u->chunk.memblock && u->chunk.memblock->length > u->chunk.index); + if ((r = pa_iochannel_read(u->io, (uint8_t*) u->chunk.memblock->data + u->chunk.index, u->chunk.memblock->length - u->chunk.index)) <= 0) { + pa_log(__FILE__": read() failed: %s\n", strerror(errno)); + return; + } + + u->chunk.length = r; + pa_source_post(u->source, &u->chunk); + u->chunk.index += r; + + if (u->chunk.index >= u->chunk.memblock->length) { + u->chunk.index = u->chunk.length = 0; + pa_memblock_unref(u->chunk.memblock); + u->chunk.memblock = NULL; + } +} + +static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) { + struct userdata *u = userdata; + assert(u); + do_read(u); +} + +int pa__init(pa_core *c, pa_module*m) { + struct userdata *u = NULL; + struct stat st; + const char *p; + int fd = -1; + pa_sample_spec ss; + pa_modargs *ma = NULL; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments\n"); + goto fail; + } + + ss = c->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &ss) < 0) { + pa_log(__FILE__": invalid sample format specification\n"); + goto fail; + } + + mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777); + + if ((fd = open(p, O_RDWR)) < 0) { + pa_log(__FILE__": open('%s'): %s\n", p, strerror(errno)); + goto fail; + } + + pa_fd_set_cloexec(fd, 1); + + if (fstat(fd, &st) < 0) { + pa_log(__FILE__": fstat('%s'): %s\n", p, strerror(errno)); + goto fail; + } + + if (!S_ISFIFO(st.st_mode)) { + pa_log(__FILE__": '%s' is not a FIFO.\n", p); + goto fail; + } + + u = pa_xmalloc0(sizeof(struct userdata)); + + u->filename = pa_xstrdup(p); + u->core = c; + + if (!(u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL))) { + pa_log(__FILE__": failed to create source.\n"); + goto fail; + } + u->source->userdata = u; + pa_source_set_owner(u->source, m); + u->source->description = pa_sprintf_malloc("Unix FIFO source '%s'", p); + assert(u->source->description); + + u->io = pa_iochannel_new(c->mainloop, fd, -1); + assert(u->io); + pa_iochannel_set_callback(u->io, io_callback, u); + + u->chunk.memblock = NULL; + u->chunk.index = u->chunk.length = 0; + + u->module = m; + m->userdata = u; + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + if (fd >= 0) + close(fd); + + pa__done(c, m); + + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata *u; + assert(c && m); + + if (!(u = m->userdata)) + return; + + if (u->chunk.memblock) + pa_memblock_unref(u->chunk.memblock); + + pa_source_disconnect(u->source); + pa_source_unref(u->source); + pa_iochannel_free(u->io); + + assert(u->filename); + unlink(u->filename); + pa_xfree(u->filename); + + pa_xfree(u); +} diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c new file mode 100644 index 00000000..469ddcee --- /dev/null +++ b/src/modules/module-protocol-stub.c @@ -0,0 +1,261 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_TCP_SOCKETS +#define SOCKET_DESCRIPTION "(TCP sockets)" +#define SOCKET_USAGE "port= loopback=" +#elif defined(USE_TCP6_SOCKETS) +#define SOCKET_DESCRIPTION "(TCP/IPv6 sockets)" +#define SOCKET_USAGE "port= loopback=" +#else +#define SOCKET_DESCRIPTION "(UNIX sockets)" +#define SOCKET_USAGE "socket=" +#endif + +#if defined(USE_PROTOCOL_SIMPLE) + #include + #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= format= channels= sink= source= playback= record= "SOCKET_USAGE) +#elif defined(USE_PROTOCOL_CLI) + #include + #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 + #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 + #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= cookie= "SOCKET_USAGE) +#elif defined(USE_PROTOCOL_ESOUND) + #include + #include + #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= source= public= cookie= "SOCKET_USAGE) +#else + #error "Broken build system" +#endif + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_VERSION(PACKAGE_VERSION) + +static const char* const valid_modargs[] = { + MODULE_ARGUMENTS +#if defined(USE_TCP_SOCKETS) || defined(USE_TCP6_SOCKETS) + "port", + "loopback", +#else + "socket", +#endif + NULL +}; + +static pa_socket_server *create_socket_server(pa_core *c, pa_modargs *ma) { + pa_socket_server *s; +#if defined(USE_TCP_SOCKETS) || defined(USE_TCP6_SOCKETS) + int loopback = 1; + uint32_t port = IPV4_PORT; + + if (pa_modargs_get_value_boolean(ma, "loopback", &loopback) < 0) { + pa_log(__FILE__": loopback= expects a boolean argument.\n"); + return NULL; + } + + if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port < 1 || port > 0xFFFF) { + pa_log(__FILE__": port= expects a numerical argument between 1 and 65535.\n"); + return NULL; + } + +#ifdef USE_TCP6_SOCKETS + if (!(s = pa_socket_server_new_ipv6(c->mainloop, loopback ? (const uint8_t*) &in6addr_loopback : (const uint8_t*) &in6addr_any, port))) + return NULL; +#else + if (!(s = pa_socket_server_new_ipv4(c->mainloop, loopback ? INADDR_LOOPBACK : INADDR_ANY, port, TCPWRAP_SERVICE))) + return NULL; +#endif + +#else + int r; + const char *v; + char tmp[PATH_MAX]; + + v = pa_modargs_get_value(ma, "socket", UNIX_SOCKET); + assert(v); + + pa_runtime_path(v, tmp, sizeof(tmp)); + + if (pa_make_secure_parent_dir(tmp) < 0) { + pa_log(__FILE__": Failed to create secure socket directory.\n"); + return NULL; + } + + if ((r = pa_unix_socket_remove_stale(tmp)) < 0) { + pa_log(__FILE__": Failed to remove stale UNIX socket '%s': %s\n", tmp, strerror(errno)); + return NULL; + } + + if (r) + pa_log(__FILE__": Removed stale UNIX socket '%s'.", tmp); + + if (!(s = pa_socket_server_new_unix(c->mainloop, tmp))) + return NULL; + +#endif + return s; +} + +int pa__init(pa_core *c, pa_module*m) { + pa_socket_server *s; + pa_modargs *ma = NULL; + int ret = -1; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": Failed to parse module arguments\n"); + goto finish; + } + + if (!(s = create_socket_server(c, ma))) + goto finish; + + if (!(m->userdata = protocol_new(c, s, m, ma))) { + pa_socket_server_unref(s); + goto finish; + } + + ret = 0; + +finish: + if (ma) + pa_modargs_free(ma); + + return ret; +} + +void pa__done(pa_core *c, pa_module*m) { + assert(c && m); + +#if defined(USE_PROTOCOL_ESOUND) + if (remove (ESD_UNIX_SOCKET_NAME) != 0) + pa_log("%s: Failed to remove %s : %s.\n", __FILE__, ESD_UNIX_SOCKET_NAME, strerror (errno)); + if (remove (ESD_UNIX_SOCKET_DIR) != 0) + pa_log("%s: Failed to remove %s : %s.\n", __FILE__, ESD_UNIX_SOCKET_DIR, strerror (errno)); +#endif + + protocol_free(m->userdata); +} diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c new file mode 100644 index 00000000..446e3974 --- /dev/null +++ b/src/modules/module-sine.c @@ -0,0 +1,182 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "module-sine-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("Sine wave generator") +PA_MODULE_USAGE("sink= frequency=") +PA_MODULE_VERSION(PACKAGE_VERSION) + +struct userdata { + pa_core *core; + pa_module *module; + pa_sink_input *sink_input; + pa_memblock *memblock; + size_t peek_index; +}; + +static const char* const valid_modargs[] = { + "sink", + "frequency", + NULL, +}; + +static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) { + struct userdata *u; + assert(i && chunk && i->userdata); + u = i->userdata; + + chunk->memblock = pa_memblock_ref(u->memblock); + chunk->index = u->peek_index; + chunk->length = u->memblock->length - u->peek_index; + return 0; +} + +static void sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { + struct userdata *u; + assert(i && chunk && length && i->userdata); + u = i->userdata; + + assert(chunk->memblock == u->memblock && length <= u->memblock->length-u->peek_index); + + u->peek_index += length; + + if (u->peek_index >= u->memblock->length) + u->peek_index = 0; +} + +static void sink_input_kill(pa_sink_input *i) { + struct userdata *u; + assert(i && i->userdata); + u = i->userdata; + + pa_sink_input_disconnect(u->sink_input); + pa_sink_input_unref(u->sink_input); + u->sink_input = NULL; + + pa_module_unload_request(u->module); +} + +static void calc_sine(float *f, size_t l, float freq) { + size_t i; + + l /= sizeof(float); + + for (i = 0; i < l; i++) + f[i] = (float) sin((double) i/l*M_PI*2*freq)/2; +} + +int pa__init(pa_core *c, pa_module*m) { + pa_modargs *ma = NULL; + struct userdata *u; + pa_sink *sink; + const char *sink_name; + pa_sample_spec ss; + uint32_t frequency; + char t[256]; + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": Failed to parse module arguments\n"); + goto fail; + } + + m->userdata = u = pa_xmalloc(sizeof(struct userdata)); + u->core = c; + u->module = m; + u->sink_input = NULL; + u->memblock = NULL; + + sink_name = pa_modargs_get_value(ma, "sink", NULL); + + if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) { + pa_log(__FILE__": No such sink.\n"); + goto fail; + } + + ss.format = PA_SAMPLE_FLOAT32; + ss.rate = sink->sample_spec.rate; + ss.channels = 1; + + frequency = 440; + if (pa_modargs_get_value_u32(ma, "frequency", &frequency) < 0 || frequency < 1 || frequency > ss.rate/2) { + pa_log(__FILE__": Invalid frequency specification\n"); + goto fail; + } + + u->memblock = pa_memblock_new(pa_bytes_per_second(&ss), c->memblock_stat); + calc_sine(u->memblock->data, u->memblock->length, frequency); + + snprintf(t, sizeof(t), "Sine Generator at %u Hz", frequency); + if (!(u->sink_input = pa_sink_input_new(sink, __FILE__, t, &ss, NULL, 0, -1))) + goto fail; + + u->sink_input->peek = sink_input_peek; + u->sink_input->drop = sink_input_drop; + u->sink_input->kill = sink_input_kill; + u->sink_input->userdata = u; + u->sink_input->owner = m; + + u->peek_index = 0; + + pa_modargs_free(ma); + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(c, m); + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata *u = m->userdata; + assert(c && m); + + if (!u) + return; + + if (u->sink_input) { + pa_sink_input_disconnect(u->sink_input); + pa_sink_input_unref(u->sink_input); + } + + if (u->memblock) + pa_memblock_unref(u->memblock); + pa_xfree(u); +} + diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c new file mode 100644 index 00000000..e0745fc0 --- /dev/null +++ b/src/modules/module-solaris.c @@ -0,0 +1,488 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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= source_name= device= record= playback= format= channels= rate= buffer_size=") + +struct userdata { + pa_sink *sink; + pa_source *source; + pa_iochannel *io; + pa_core *core; + pa_signal_event *sig; + + pa_memchunk memchunk, silence; + + uint32_t sample_size; + uint32_t buffer_size; + unsigned int written_bytes, read_bytes; + + int fd; + pa_module *module; +}; + +static const char* const valid_modargs[] = { + "sink_name", + "source_name", + "device", + "record", + "playback", + "buffer_size", + "format", + "rate", + "channels", + NULL +}; + +#define DEFAULT_SINK_NAME "solaris_output" +#define DEFAULT_SOURCE_NAME "solaris_input" +#define DEFAULT_DEVICE "/dev/audio" + +#define CHUNK_SIZE 2048 + +static void update_usage(struct userdata *u) { + pa_module_set_used(u->module, + (u->sink ? pa_idxset_size(u->sink->inputs) : 0) + + (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0) + + (u->source ? pa_idxset_size(u->source->outputs) : 0)); +} + +static void do_write(struct userdata *u) { + audio_info_t info; + int err; + pa_memchunk *memchunk; + size_t len; + ssize_t r; + + assert(u); + + if (!u->sink || !pa_iochannel_is_writable(u->io)) + return; + + update_usage(u); + + err = ioctl(u->fd, AUDIO_GETINFO, &info); + assert(err >= 0); + + /* + * Since we cannot modify the size of the output buffer we fake it + * by not filling it more than u->buffer_size. + */ + len = u->buffer_size; + len -= u->written_bytes - (info.play.samples * u->sample_size); + + /* + * Do not fill more than half the buffer in one chunk since we only + * get notifications upon completion of entire chunks. + */ + if (len > (u->buffer_size / 2)) + len = u->buffer_size / 2; + + if (len < u->sample_size) + return; + + memchunk = &u->memchunk; + + if (!memchunk->length) + if (pa_sink_render(u->sink, len, memchunk) < 0) + memchunk = &u->silence; + + assert(memchunk->memblock); + assert(memchunk->memblock->data); + assert(memchunk->length); + + if (memchunk->length < len) + len = memchunk->length; + + if ((r = pa_iochannel_write(u->io, (uint8_t*) memchunk->memblock->data + memchunk->index, len)) < 0) { + pa_log(__FILE__": write() failed: %s\n", strerror(errno)); + return; + } + + if (memchunk == &u->silence) + assert(r % u->sample_size == 0); + else { + u->memchunk.index += r; + u->memchunk.length -= r; + + if (u->memchunk.length <= 0) { + pa_memblock_unref(u->memchunk.memblock); + u->memchunk.memblock = NULL; + } + } + + u->written_bytes += r; + + /* + * Write 0 bytes which will generate a SIGPOLL when "played". + */ + if (write(u->fd, NULL, 0) < 0) { + pa_log(__FILE__": write() failed: %s\n", strerror(errno)); + return; + } +} + +static void do_read(struct userdata *u) { + pa_memchunk memchunk; + int err, l; + ssize_t r; + assert(u); + + if (!u->source || !pa_iochannel_is_readable(u->io)) + return; + + update_usage(u); + + err = ioctl(u->fd, I_NREAD, &l); + assert(err >= 0); + + memchunk.memblock = pa_memblock_new(l, u->core->memblock_stat); + assert(memchunk.memblock); + if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) { + pa_memblock_unref(memchunk.memblock); + if (errno != EAGAIN) + pa_log(__FILE__": read() failed: %s\n", strerror(errno)); + return; + } + + assert(r <= (ssize_t) memchunk.memblock->length); + memchunk.length = memchunk.memblock->length = r; + memchunk.index = 0; + + pa_source_post(u->source, &memchunk); + pa_memblock_unref(memchunk.memblock); + + u->read_bytes += r; +} + +static void io_callback(pa_iochannel *io, void*userdata) { + struct userdata *u = userdata; + assert(u); + do_write(u); + do_read(u); +} + +void sig_callback(pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata) { + struct userdata *u = userdata; + assert(u); + do_write(u); +} + +static pa_usec_t sink_get_latency_cb(pa_sink *s) { + pa_usec_t r = 0; + audio_info_t info; + int err; + struct userdata *u = s->userdata; + assert(s && u && u->sink); + + err = ioctl(u->fd, AUDIO_GETINFO, &info); + assert(err >= 0); + + r += pa_bytes_to_usec(u->written_bytes, &s->sample_spec); + r -= pa_bytes_to_usec(info.play.samples * u->sample_size, &s->sample_spec); + + if (u->memchunk.memblock) + r += pa_bytes_to_usec(u->memchunk.length, &s->sample_spec); + + return r; +} + +static pa_usec_t source_get_latency_cb(pa_source *s) { + pa_usec_t r = 0; + struct userdata *u = s->userdata; + audio_info_t info; + int err; + assert(s && u && u->source); + + err = ioctl(u->fd, AUDIO_GETINFO, &info); + assert(err >= 0); + + r += pa_bytes_to_usec(info.record.samples * u->sample_size, &s->sample_spec); + r -= pa_bytes_to_usec(u->read_bytes, &s->sample_spec); + + return r; +} + +static int pa_solaris_auto_format(int fd, int mode, pa_sample_spec *ss) { + audio_info_t info; + + AUDIO_INITINFO(&info); + + if (mode != O_RDONLY) { + info.play.sample_rate = ss->rate; + info.play.channels = ss->channels; + switch (ss->format) { + case PA_SAMPLE_U8: + info.play.precision = 8; + info.play.encoding = AUDIO_ENCODING_LINEAR; + break; + case PA_SAMPLE_ALAW: + info.play.precision = 8; + info.play.encoding = AUDIO_ENCODING_ALAW; + break; + case PA_SAMPLE_ULAW: + info.play.precision = 8; + info.play.encoding = AUDIO_ENCODING_ULAW; + break; + case PA_SAMPLE_S16NE: + info.play.precision = 16; + info.play.encoding = AUDIO_ENCODING_LINEAR; + break; + default: + return -1; + } + } + + if (mode != O_WRONLY) { + info.record.sample_rate = ss->rate; + info.record.channels = ss->channels; + switch (ss->format) { + case PA_SAMPLE_U8: + info.record.precision = 8; + info.record.encoding = AUDIO_ENCODING_LINEAR; + break; + case PA_SAMPLE_ALAW: + info.record.precision = 8; + info.record.encoding = AUDIO_ENCODING_ALAW; + break; + case PA_SAMPLE_ULAW: + info.record.precision = 8; + info.record.encoding = AUDIO_ENCODING_ULAW; + break; + case PA_SAMPLE_S16NE: + info.record.precision = 16; + info.record.encoding = AUDIO_ENCODING_LINEAR; + break; + default: + return -1; + } + } + + if (ioctl(fd, AUDIO_SETINFO, &info) < 0) { + if (errno == EINVAL) + pa_log(__FILE__": AUDIO_SETINFO: Unsupported sample format.\n"); + else + pa_log(__FILE__": AUDIO_SETINFO: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +static int pa_solaris_set_buffer(int fd, int buffer_size) { + audio_info_t info; + + AUDIO_INITINFO(&info); + + info.record.buffer_size = buffer_size; + + if (ioctl(fd, AUDIO_SETINFO, &info) < 0) { + if (errno == EINVAL) + pa_log(__FILE__": AUDIO_SETINFO: Unsupported buffer size.\n"); + else + pa_log(__FILE__": AUDIO_SETINFO: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +int pa__init(pa_core *c, pa_module*m) { + struct userdata *u = NULL; + const char *p; + int fd = -1; + int buffer_size; + int mode; + int record = 1, playback = 1; + pa_sample_spec ss; + pa_modargs *ma = NULL; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments.\n"); + goto fail; + } + + if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) { + pa_log(__FILE__": record= and playback= expect numeric argument.\n"); + goto fail; + } + + if (!playback && !record) { + pa_log(__FILE__": neither playback nor record enabled for device.\n"); + goto fail; + } + + mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0)); + + buffer_size = 16384; + if (pa_modargs_get_value_s32(ma, "buffer_size", &buffer_size) < 0) { + pa_log(__FILE__": failed to parse buffer size argument\n"); + goto fail; + } + + ss = c->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &ss) < 0) { + pa_log(__FILE__": failed to parse sample specification\n"); + goto fail; + } + + if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0) + goto fail; + + pa_log_info(__FILE__": device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR")); + + if (pa_solaris_auto_format(fd, mode, &ss) < 0) + goto fail; + + if ((mode != O_WRONLY) && (buffer_size >= 1)) + if (pa_solaris_set_buffer(fd, buffer_size) < 0) + goto fail; + + u = pa_xmalloc(sizeof(struct userdata)); + u->core = c; + + if (mode != O_WRONLY) { + u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL); + assert(u->source); + u->source->userdata = u; + u->source->get_latency = source_get_latency_cb; + pa_source_set_owner(u->source, m); + u->source->description = pa_sprintf_malloc("Solaris PCM on '%s'", p); + } else + u->source = NULL; + + if (mode != O_RDONLY) { + u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL); + assert(u->sink); + u->sink->get_latency = sink_get_latency_cb; + u->sink->userdata = u; + pa_sink_set_owner(u->sink, m); + u->sink->description = pa_sprintf_malloc("Solaris PCM on '%s'", p); + } else + u->sink = NULL; + + assert(u->source || u->sink); + + u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0); + assert(u->io); + pa_iochannel_set_callback(u->io, io_callback, u); + u->fd = fd; + + u->memchunk.memblock = NULL; + u->memchunk.length = 0; + u->sample_size = pa_frame_size(&ss); + u->buffer_size = buffer_size; + + u->silence.memblock = pa_memblock_new(u->silence.length = CHUNK_SIZE, u->core->memblock_stat); + assert(u->silence.memblock); + pa_silence_memblock(u->silence.memblock, &ss); + u->silence.index = 0; + + u->written_bytes = 0; + u->read_bytes = 0; + + u->module = m; + m->userdata = u; + + u->sig = pa_signal_new(SIGPOLL, sig_callback, u); + assert(u->sig); + ioctl(u->fd, I_SETSIG, S_MSG); + + pa_modargs_free(ma); + + return 0; + +fail: + if (fd >= 0) + close(fd); + + if (ma) + pa_modargs_free(ma); + + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata *u; + assert(c && m); + + if (!(u = m->userdata)) + return; + + ioctl(u->fd, I_SETSIG, 0); + pa_signal_free(u->sig); + + if (u->memchunk.memblock) + pa_memblock_unref(u->memchunk.memblock); + if (u->silence.memblock) + pa_memblock_unref(u->silence.memblock); + + if (u->sink) { + pa_sink_disconnect(u->sink); + pa_sink_unref(u->sink); + } + + if (u->source) { + pa_source_disconnect(u->source); + pa_source_unref(u->source); + } + + pa_iochannel_free(u->io); + pa_xfree(u); +} diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c new file mode 100644 index 00000000..5ee10fda --- /dev/null +++ b/src/modules/module-tunnel.c @@ -0,0 +1,688 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef TUNNEL_SINK +#include "module-tunnel-sink-symdef.h" +PA_MODULE_DESCRIPTION("Tunnel module for sinks") +PA_MODULE_USAGE("server=
sink= cookie= format= channels= rate= sink_name=") +#else +#include "module-tunnel-source-symdef.h" +PA_MODULE_DESCRIPTION("Tunnel module for sources") +PA_MODULE_USAGE("server=
source= cookie= format= channels= rate= source_name=") +#endif + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_VERSION(PACKAGE_VERSION) + +#define DEFAULT_SINK_NAME "tunnel" +#define DEFAULT_SOURCE_NAME "tunnel" + +#define DEFAULT_TLENGTH (44100*2*2/10) //(10240*8) +#define DEFAULT_MAXLENGTH ((DEFAULT_TLENGTH*3)/2) +#define DEFAULT_MINREQ 512 +#define DEFAULT_PREBUF (DEFAULT_TLENGTH-DEFAULT_MINREQ) +#define DEFAULT_FRAGSIZE 1024 + +#define DEFAULT_TIMEOUT 5 + +#define LATENCY_INTERVAL 10 + +static const char* const valid_modargs[] = { + "server", + "cookie", + "format", + "channels", + "rate", +#ifdef TUNNEL_SINK + "sink_name", + "sink", +#else + "source_name", + "source", +#endif + NULL, +}; + +static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); + +#ifdef TUNNEL_SINK +static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +#endif + +static const pa_pdispatch_callback command_table[PA_COMMAND_MAX] = { +#ifdef TUNNEL_SINK + [PA_COMMAND_REQUEST] = command_request, +#endif + [PA_COMMAND_PLAYBACK_STREAM_KILLED] = command_stream_killed, + [PA_COMMAND_RECORD_STREAM_KILLED] = command_stream_killed +}; + +struct userdata { + pa_socket_client *client; + pa_pstream *pstream; + pa_pdispatch *pdispatch; + + char *server_name; +#ifdef TUNNEL_SINK + char *sink_name; + pa_sink *sink; + uint32_t requested_bytes; +#else + char *source_name; + pa_source *source; +#endif + + pa_module *module; + pa_core *core; + + uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH]; + + uint32_t ctag; + uint32_t device_index; + uint32_t channel; + + pa_usec_t host_latency; + + pa_time_event *time_event; + + int auth_cookie_in_property; +}; + +static void close_stuff(struct userdata *u) { + assert(u); + + if (u->pstream) { + pa_pstream_close(u->pstream); + pa_pstream_unref(u->pstream); + u->pstream = NULL; + } + + if (u->pdispatch) { + pa_pdispatch_unref(u->pdispatch); + u->pdispatch = NULL; + } + + if (u->client) { + pa_socket_client_unref(u->client); + u->client = NULL; + } + +#ifdef TUNNEL_SINK + if (u->sink) { + pa_sink_disconnect(u->sink); + pa_sink_unref(u->sink); + u->sink = NULL; + } +#else + if (u->source) { + pa_source_disconnect(u->source); + pa_source_unref(u->source); + u->source = NULL; + } +#endif + + if (u->time_event) { + u->core->mainloop->time_free(u->time_event); + u->time_event = NULL; + } +} + +static void die(struct userdata *u) { + assert(u); + close_stuff(u); + pa_module_unload_request(u->module); +} + +static void command_stream_killed(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + struct userdata *u = userdata; + assert(pd && t && u && u->pdispatch == pd); + + pa_log(__FILE__": stream killed\n"); + die(u); +} + +#ifdef TUNNEL_SINK +static void send_prebuf_request(struct userdata *u) { + pa_tagstruct *t; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_PREBUF_PLAYBACK_STREAM); + pa_tagstruct_putu32(t, u->ctag++); + pa_tagstruct_putu32(t, u->channel); + pa_pstream_send_tagstruct(u->pstream, t); +} + +static void send_bytes(struct userdata *u) { + assert(u); + + if (!u->pstream) + return; + + while (u->requested_bytes > 0) { + pa_memchunk chunk; + if (pa_sink_render(u->sink, u->requested_bytes, &chunk) < 0) { + + + if (u->requested_bytes >= DEFAULT_TLENGTH-DEFAULT_PREBUF) + send_prebuf_request(u); + + return; + } + + pa_pstream_send_memblock(u->pstream, u->channel, 0, &chunk); + pa_memblock_unref(chunk.memblock); + + if (chunk.length > u->requested_bytes) + u->requested_bytes = 0; + else + u->requested_bytes -= chunk.length; + } +} + +static void command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + struct userdata *u = userdata; + uint32_t bytes, channel; + assert(pd && command == PA_COMMAND_REQUEST && t && u && u->pdispatch == pd); + + if (pa_tagstruct_getu32(t, &channel) < 0 || + pa_tagstruct_getu32(t, &bytes) < 0 || + !pa_tagstruct_eof(t)) { + pa_log(__FILE__": invalid protocol reply\n"); + die(u); + return; + } + + if (channel != u->channel) { + pa_log(__FILE__": recieved data for invalid channel\n"); + die(u); + return; + } + + u->requested_bytes += bytes; + send_bytes(u); +} + +#endif + +static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + struct userdata *u = userdata; + pa_usec_t buffer_usec, sink_usec, source_usec, transport_usec; + int playing; + uint32_t queue_length; + uint64_t counter; + struct timeval local, remote, now; + assert(pd && u); + + if (command != PA_COMMAND_REPLY) { + if (command == PA_COMMAND_ERROR) + pa_log(__FILE__": failed to get latency.\n"); + else + pa_log(__FILE__": protocol error.\n"); + die(u); + return; + } + + if (pa_tagstruct_get_usec(t, &buffer_usec) < 0 || + pa_tagstruct_get_usec(t, &sink_usec) < 0 || + pa_tagstruct_get_usec(t, &source_usec) < 0 || + pa_tagstruct_get_boolean(t, &playing) < 0 || + pa_tagstruct_getu32(t, &queue_length) < 0 || + pa_tagstruct_get_timeval(t, &local) < 0 || + pa_tagstruct_get_timeval(t, &remote) < 0 || + pa_tagstruct_getu64(t, &counter) < 0 || + !pa_tagstruct_eof(t)) { + pa_log(__FILE__": invalid reply.\n"); + die(u); + return; + } + + pa_gettimeofday(&now); + + if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) { + /* local and remote seem to have synchronized clocks */ +#ifdef TUNNEL_SINK + transport_usec = pa_timeval_diff(&remote, &local); +#else + transport_usec = pa_timeval_diff(&now, &remote); +#endif + } else + transport_usec = pa_timeval_diff(&now, &local)/2; + +#ifdef TUNNEL_SINK + u->host_latency = sink_usec + transport_usec; +#else + u->host_latency = source_usec + transport_usec; + if (u->host_latency > sink_usec) + u->host_latency -= sink_usec; + else + u->host_latency = 0; +#endif + +/* pa_log(__FILE__": estimated host latency: %0.0f usec\n", (double) u->host_latency); */ +} + +static void request_latency(struct userdata *u) { + pa_tagstruct *t; + struct timeval now; + uint32_t tag; + assert(u); + + t = pa_tagstruct_new(NULL, 0); +#ifdef TUNNEL_SINK + pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY); +#else + pa_tagstruct_putu32(t, PA_COMMAND_GET_RECORD_LATENCY); +#endif + pa_tagstruct_putu32(t, tag = u->ctag++); + pa_tagstruct_putu32(t, u->channel); + + pa_gettimeofday(&now); + pa_tagstruct_put_timeval(t, &now); + pa_tagstruct_putu64(t, 0); + + pa_pstream_send_tagstruct(u->pstream, t); + pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u); +} + +static void create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + struct userdata *u = userdata; + assert(pd && u && u->pdispatch == pd); + + if (command != PA_COMMAND_REPLY) { + if (command == PA_COMMAND_ERROR) + pa_log(__FILE__": failed to create stream.\n"); + else + pa_log(__FILE__": protocol error.\n"); + die(u); + return; + } + + if (pa_tagstruct_getu32(t, &u->channel) < 0 || + pa_tagstruct_getu32(t, &u->device_index) < 0 || +#ifdef TUNNEL_SINK + pa_tagstruct_getu32(t, &u->requested_bytes) < 0 || +#endif + !pa_tagstruct_eof(t)) { + pa_log(__FILE__": invalid reply.\n"); + die(u); + return; + } + + request_latency(u); +#ifdef TUNNEL_SINK + send_bytes(u); +#endif +} + +static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct userdata *u = userdata; + pa_tagstruct *reply; + char name[256], un[128], hn[128]; + assert(pd && u && u->pdispatch == pd); + + if (command != PA_COMMAND_REPLY || !pa_tagstruct_eof(t)) { + if (command == PA_COMMAND_ERROR) + pa_log(__FILE__": failed to authenticate\n"); + else + pa_log(__FILE__": protocol error.\n"); + die(u); + return; + } +#ifdef TUNNEL_SINK + snprintf(name, sizeof(name), "Tunnel from host '%s', user '%s', sink '%s'", + pa_get_host_name(hn, sizeof(hn)), + pa_get_user_name(un, sizeof(un)), + u->sink->name); +#else + snprintf(name, sizeof(name), "Tunnel from host '%s', user '%s', source '%s'", + pa_get_host_name(hn, sizeof(hn)), + pa_get_user_name(un, sizeof(un)), + u->source->name); +#endif + + reply = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME); + pa_tagstruct_putu32(reply, tag = u->ctag++); + pa_tagstruct_puts(reply, name); + pa_pstream_send_tagstruct(u->pstream, reply); + /* We ignore the server's reply here */ + + reply = pa_tagstruct_new(NULL, 0); +#ifdef TUNNEL_SINK + pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_PLAYBACK_STREAM); + pa_tagstruct_putu32(reply, tag = u->ctag++); + pa_tagstruct_puts(reply, name); + pa_tagstruct_put_sample_spec(reply, &u->sink->sample_spec); + pa_tagstruct_putu32(reply, PA_INVALID_INDEX); + pa_tagstruct_puts(reply, u->sink_name); + pa_tagstruct_putu32(reply, DEFAULT_MAXLENGTH); + pa_tagstruct_put_boolean(reply, 0); + pa_tagstruct_putu32(reply, DEFAULT_TLENGTH); + pa_tagstruct_putu32(reply, DEFAULT_PREBUF); + pa_tagstruct_putu32(reply, DEFAULT_MINREQ); + pa_tagstruct_putu32(reply, PA_VOLUME_NORM); +#else + pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_RECORD_STREAM); + pa_tagstruct_putu32(reply, tag = u->ctag++); + pa_tagstruct_puts(reply, name); + pa_tagstruct_put_sample_spec(reply, &u->source->sample_spec); + pa_tagstruct_putu32(reply, PA_INVALID_INDEX); + pa_tagstruct_puts(reply, u->source_name); + pa_tagstruct_putu32(reply, DEFAULT_MAXLENGTH); + pa_tagstruct_put_boolean(reply, 0); + pa_tagstruct_putu32(reply, DEFAULT_FRAGSIZE); +#endif + + pa_pstream_send_tagstruct(u->pstream, reply); + pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u); +} + +static void pstream_die_callback(pa_pstream *p, void *userdata) { + struct userdata *u = userdata; + assert(p && u); + + pa_log(__FILE__": stream died.\n"); + die(u); +} + + +static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) { + struct userdata *u = userdata; + assert(p && packet && u); + + if (pa_pdispatch_run(u->pdispatch, packet, u) < 0) { + pa_log(__FILE__": invalid packet\n"); + die(u); + } +} + +#ifndef TUNNEL_SINK +static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata) { + struct userdata *u = userdata; + assert(p && chunk && u); + + if (channel != u->channel) { + pa_log(__FILE__": recieved memory block on bad channel.\n"); + die(u); + return; + } + + pa_source_post(u->source, chunk); +} +#endif + +static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) { + struct userdata *u = userdata; + pa_tagstruct *t; + uint32_t tag; + assert(sc && u && u->client == sc); + + pa_socket_client_unref(u->client); + u->client = NULL; + + if (!io) { + pa_log(__FILE__": connection failed.\n"); + pa_module_unload_request(u->module); + return; + } + + u->pstream = pa_pstream_new(u->core->mainloop, io, u->core->memblock_stat); + u->pdispatch = pa_pdispatch_new(u->core->mainloop, command_table, PA_COMMAND_MAX); + + pa_pstream_set_die_callback(u->pstream, pstream_die_callback, u); + pa_pstream_set_recieve_packet_callback(u->pstream, pstream_packet_callback, u); +#ifndef TUNNEL_SINK + pa_pstream_set_recieve_memblock_callback(u->pstream, pstream_memblock_callback, u); +#endif + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_AUTH); + pa_tagstruct_putu32(t, tag = u->ctag++); + pa_tagstruct_put_arbitrary(t, u->auth_cookie, sizeof(u->auth_cookie)); + pa_pstream_send_tagstruct(u->pstream, t); + pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, u); + +} + +#ifdef TUNNEL_SINK +static void sink_notify(pa_sink*sink) { + struct userdata *u; + assert(sink && sink->userdata); + u = sink->userdata; + + send_bytes(u); +} + +static pa_usec_t sink_get_latency(pa_sink *sink) { + struct userdata *u; + uint32_t l; + pa_usec_t usec = 0; + assert(sink && sink->userdata); + u = sink->userdata; + + l = DEFAULT_TLENGTH; + + if (l > u->requested_bytes) { + l -= u->requested_bytes; + usec += pa_bytes_to_usec(l, &u->sink->sample_spec); + } + + usec += u->host_latency; + + return usec; +} +#else +static pa_usec_t source_get_latency(pa_source *source) { + struct userdata *u; + assert(source && source->userdata); + u = source->userdata; + + return u->host_latency; +} +#endif + +static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { + struct userdata *u = userdata; + struct timeval ntv; + assert(m && e && u); + + request_latency(u); + + pa_gettimeofday(&ntv); + ntv.tv_sec += LATENCY_INTERVAL; + m->time_restart(e, &ntv); +} + +static int load_key(struct userdata *u, const char*fn) { + assert(u); + + u->auth_cookie_in_property = 0; + + if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) { + pa_log_debug(__FILE__": using already loaded auth cookie.\n"); + pa_authkey_prop_ref(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME); + u->auth_cookie_in_property = 1; + return 0; + } + + if (!fn) + fn = PA_NATIVE_COOKIE_FILE; + + if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0) + return -1; + + pa_log_debug(__FILE__": loading cookie from disk.\n"); + + if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) + u->auth_cookie_in_property = 1; + + return 0; +} + +int pa__init(pa_core *c, pa_module*m) { + pa_modargs *ma = NULL; + struct userdata *u = NULL; + pa_sample_spec ss; + struct timeval ntv; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments\n"); + goto fail; + } + + u = pa_xmalloc(sizeof(struct userdata)); + m->userdata = u; + u->module = m; + u->core = c; + u->client = NULL; + u->pdispatch = NULL; + u->pstream = NULL; + u->server_name = NULL; +#ifdef TUNNEL_SINK + u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));; + u->sink = NULL; + u->requested_bytes = 0; +#else + u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));; + u->source = NULL; +#endif + u->ctag = 1; + u->device_index = u->channel = PA_INVALID_INDEX; + u->host_latency = 0; + u->auth_cookie_in_property = 0; + u->time_event = NULL; + + if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0) + goto fail; + + if (!(u->server_name = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL)))) { + pa_log(__FILE__": no server specified.\n"); + goto fail; + } + + ss = c->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &ss) < 0) { + pa_log(__FILE__": invalid sample format specification\n"); + goto fail; + } + + if (!(u->client = pa_socket_client_new_string(c->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) { + pa_log(__FILE__": failed to connect to server '%s'\n", u->server_name); + goto fail; + } + + if (!u->client) + goto fail; + + pa_socket_client_set_callback(u->client, on_connection, u); + +#ifdef TUNNEL_SINK + if (!(u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) { + pa_log(__FILE__": failed to create sink.\n"); + goto fail; + } + + u->sink->notify = sink_notify; + u->sink->get_latency = sink_get_latency; + u->sink->userdata = u; + u->sink->description = pa_sprintf_malloc("Tunnel to '%s%s%s'", u->sink_name ? u->sink_name : "", u->sink_name ? "@" : "", u->server_name); + + pa_sink_set_owner(u->sink, m); +#else + if (!(u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL))) { + pa_log(__FILE__": failed to create source.\n"); + goto fail; + } + + u->source->get_latency = source_get_latency; + u->source->userdata = u; + u->source->description = pa_sprintf_malloc("Tunnel to '%s%s%s'", u->source_name ? u->source_name : "", u->source_name ? "@" : "", u->server_name); + + pa_source_set_owner(u->source, m); +#endif + + pa_gettimeofday(&ntv); + ntv.tv_sec += LATENCY_INTERVAL; + u->time_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, u); + + pa_modargs_free(ma); + + return 0; + +fail: + pa__done(c, m); + + if (ma) + pa_modargs_free(ma); + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata* u; + assert(c && m); + + if (!(u = m->userdata)) + return; + + close_stuff(u); + + if (u->auth_cookie_in_property) + pa_authkey_prop_unref(c, PA_NATIVE_COOKIE_PROPERTY_NAME); + +#ifdef TUNNEL_SINK + pa_xfree(u->sink_name); +#else + pa_xfree(u->source_name); +#endif + pa_xfree(u->server_name); + + pa_xfree(u); +} + + diff --git a/src/modules/module-waveout.c b/src/modules/module-waveout.c new file mode 100644 index 00000000..8809c31d --- /dev/null +++ b/src/modules/module-waveout.c @@ -0,0 +1,583 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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= source_name= record= playback= format= channels= rate= fragments= fragment_size=") + +#define DEFAULT_SINK_NAME "wave_output" +#define DEFAULT_SOURCE_NAME "wave_input" + +struct userdata { + pa_sink *sink; + pa_source *source; + pa_core *core; + pa_time_event *event; + pa_defer_event *defer; + pa_usec_t poll_timeout; + + uint32_t fragments, fragment_size; + + uint32_t free_ofrags, free_ifrags; + + DWORD written_bytes; + + int cur_ohdr, cur_ihdr; + unsigned int oremain; + WAVEHDR *ohdrs, *ihdrs; + pa_memchunk silence; + + HWAVEOUT hwo; + HWAVEIN hwi; + pa_module *module; + + CRITICAL_SECTION crit; +}; + +static const char* const valid_modargs[] = { + "sink_name", + "source_name", + "record", + "playback", + "fragments", + "fragment_size", + "format", + "rate", + "channels", + NULL +}; + +static void update_usage(struct userdata *u) { + pa_module_set_used(u->module, + (u->sink ? pa_idxset_size(u->sink->inputs) : 0) + + (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0) + + (u->source ? pa_idxset_size(u->source->outputs) : 0)); +} + +static void do_write(struct userdata *u) +{ + uint32_t free_frags, remain; + pa_memchunk memchunk, *cur_chunk; + WAVEHDR *hdr; + MMRESULT res; + + if (!u->sink) + return; + + EnterCriticalSection(&u->crit); + + free_frags = u->free_ofrags; + u->free_ofrags = 0; + + LeaveCriticalSection(&u->crit); + + while (free_frags) { + hdr = &u->ohdrs[u->cur_ohdr]; + if (hdr->dwFlags & WHDR_PREPARED) + waveOutUnprepareHeader(u->hwo, hdr, sizeof(WAVEHDR)); + + remain = u->oremain; + while (remain) { + cur_chunk = &memchunk; + + if (pa_sink_render(u->sink, remain, cur_chunk) < 0) { + /* + * Don't fill with silence unless we're getting close to + * underflowing. + */ + if (free_frags > u->fragments/2) + cur_chunk = &u->silence; + else { + EnterCriticalSection(&u->crit); + + u->free_ofrags += free_frags; + + LeaveCriticalSection(&u->crit); + + u->oremain = remain; + return; + } + } + + assert(cur_chunk->memblock); + assert(cur_chunk->memblock->data); + assert(cur_chunk->length); + + memcpy(hdr->lpData + u->fragment_size - remain, + (char*)cur_chunk->memblock->data + cur_chunk->index, + (cur_chunk->length < remain)?cur_chunk->length:remain); + + remain -= (cur_chunk->length < remain)?cur_chunk->length:remain; + + if (cur_chunk != &u->silence) { + pa_memblock_unref(cur_chunk->memblock); + cur_chunk->memblock = NULL; + } + } + + res = waveOutPrepareHeader(u->hwo, hdr, sizeof(WAVEHDR)); + if (res != MMSYSERR_NOERROR) { + pa_log_error(__FILE__ ": ERROR: Unable to prepare waveOut block: %d\n", + res); + } + res = waveOutWrite(u->hwo, hdr, sizeof(WAVEHDR)); + if (res != MMSYSERR_NOERROR) { + pa_log_error(__FILE__ ": ERROR: Unable to write waveOut block: %d\n", + res); + } + + u->written_bytes += u->fragment_size; + + free_frags--; + u->cur_ohdr++; + u->cur_ohdr %= u->fragments; + u->oremain = u->fragment_size; + } +} + +static void do_read(struct userdata *u) +{ + uint32_t free_frags; + pa_memchunk memchunk; + WAVEHDR *hdr; + MMRESULT res; + + if (!u->source) + return; + + EnterCriticalSection(&u->crit); + + free_frags = u->free_ifrags; + u->free_ifrags = 0; + + LeaveCriticalSection(&u->crit); + + while (free_frags) { + hdr = &u->ihdrs[u->cur_ihdr]; + if (hdr->dwFlags & WHDR_PREPARED) + waveInUnprepareHeader(u->hwi, hdr, sizeof(WAVEHDR)); + + if (hdr->dwBytesRecorded) { + memchunk.memblock = pa_memblock_new(hdr->dwBytesRecorded, u->core->memblock_stat); + assert(memchunk.memblock); + + memcpy((char*)memchunk.memblock->data, hdr->lpData, hdr->dwBytesRecorded); + + memchunk.length = memchunk.memblock->length = hdr->dwBytesRecorded; + memchunk.index = 0; + + pa_source_post(u->source, &memchunk); + pa_memblock_unref(memchunk.memblock); + } + + res = waveInPrepareHeader(u->hwi, hdr, sizeof(WAVEHDR)); + if (res != MMSYSERR_NOERROR) { + pa_log_error(__FILE__ ": ERROR: Unable to prepare waveIn block: %d\n", + res); + } + res = waveInAddBuffer(u->hwi, hdr, sizeof(WAVEHDR)); + if (res != MMSYSERR_NOERROR) { + pa_log_error(__FILE__ ": ERROR: Unable to add waveIn block: %d\n", + res); + } + + free_frags--; + u->cur_ihdr++; + u->cur_ihdr %= u->fragments; + } +} + +static void poll_cb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) { + struct userdata *u = userdata; + struct timeval ntv; + + assert(u); + + update_usage(u); + + do_write(u); + do_read(u); + + pa_gettimeofday(&ntv); + pa_timeval_add(&ntv, u->poll_timeout); + + a->time_restart(e, &ntv); +} + +static void defer_cb(pa_mainloop_api*a, pa_defer_event *e, void *userdata) { + struct userdata *u = userdata; + + assert(u); + + a->defer_enable(e, 0); + + do_write(u); + do_read(u); +} + +static void CALLBACK chunk_done_cb(HWAVEOUT hwo, UINT msg, DWORD_PTR inst, DWORD param1, DWORD param2) { + struct userdata *u = (struct userdata *)inst; + + if (msg != WOM_DONE) + return; + + EnterCriticalSection(&u->crit); + + u->free_ofrags++; + assert(u->free_ofrags <= u->fragments); + + LeaveCriticalSection(&u->crit); +} + +static void CALLBACK chunk_ready_cb(HWAVEIN hwi, UINT msg, DWORD_PTR inst, DWORD param1, DWORD param2) { + struct userdata *u = (struct userdata *)inst; + + if (msg != WIM_DATA) + return; + + EnterCriticalSection(&u->crit); + + u->free_ifrags++; + assert(u->free_ifrags <= u->fragments); + + LeaveCriticalSection(&u->crit); +} + +static pa_usec_t sink_get_latency_cb(pa_sink *s) { + struct userdata *u = s->userdata; + uint32_t free_frags; + MMTIME mmt; + assert(s && u && u->sink); + + memset(&mmt, 0, sizeof(mmt)); + mmt.wType = TIME_BYTES; + if (waveOutGetPosition(u->hwo, &mmt, sizeof(mmt)) == MMSYSERR_NOERROR) + return pa_bytes_to_usec(u->written_bytes - mmt.u.cb, &s->sample_spec); + else { + EnterCriticalSection(&u->crit); + + free_frags = u->free_ofrags; + + LeaveCriticalSection(&u->crit); + + return pa_bytes_to_usec((u->fragments - free_frags) * u->fragment_size, + &s->sample_spec); + } +} + +static pa_usec_t source_get_latency_cb(pa_source *s) { + pa_usec_t r = 0; + struct userdata *u = s->userdata; + uint32_t free_frags; + assert(s && u && u->sink); + + EnterCriticalSection(&u->crit); + + free_frags = u->free_ifrags; + + LeaveCriticalSection(&u->crit); + + r += pa_bytes_to_usec((free_frags + 1) * u->fragment_size, &s->sample_spec); + + fprintf(stderr, "Latency: %d us\n", (int)r); + + return r; +} + +static void notify_sink_cb(pa_sink *s) { + struct userdata *u = s->userdata; + assert(u); + + u->core->mainloop->defer_enable(u->defer, 1); +} + +static void notify_source_cb(pa_source *s) { + struct userdata *u = s->userdata; + assert(u); + + u->core->mainloop->defer_enable(u->defer, 1); +} + +static int ss_to_waveformat(pa_sample_spec *ss, LPWAVEFORMATEX wf) { + wf->wFormatTag = WAVE_FORMAT_PCM; + + if (ss->channels > 2) { + pa_log_error(__FILE__": ERROR: More than two channels not supported.\n"); + return -1; + } + + wf->nChannels = ss->channels; + + switch (ss->rate) { + case 8000: + case 11025: + case 22005: + case 44100: + break; + default: + pa_log_error(__FILE__": ERROR: Unsupported sample rate.\n"); + return -1; + } + + wf->nSamplesPerSec = ss->rate; + + if (ss->format == PA_SAMPLE_U8) + wf->wBitsPerSample = 8; + else if (ss->format == PA_SAMPLE_S16NE) + wf->wBitsPerSample = 16; + else { + pa_log_error(__FILE__": ERROR: Unsupported sample format.\n"); + return -1; + } + + wf->nBlockAlign = wf->nChannels * wf->wBitsPerSample/8; + wf->nAvgBytesPerSec = wf->nSamplesPerSec * wf->nBlockAlign; + + wf->cbSize = 0; + + return 0; +} + +int pa__init(pa_core *c, pa_module*m) { + struct userdata *u = NULL; + HWAVEOUT hwo = INVALID_HANDLE_VALUE; + HWAVEIN hwi = INVALID_HANDLE_VALUE; + WAVEFORMATEX wf; + int nfrags, frag_size; + int record = 1, playback = 1; + pa_sample_spec ss; + pa_modargs *ma = NULL; + unsigned int i; + struct timeval tv; + + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments.\n"); + goto fail; + } + + if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) { + pa_log(__FILE__": record= and playback= expect boolean argument.\n"); + goto fail; + } + + if (!playback && !record) { + pa_log(__FILE__": neither playback nor record enabled for device.\n"); + goto fail; + } + + nfrags = 20; + frag_size = 1024; + if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) { + pa_log(__FILE__": failed to parse fragments arguments\n"); + goto fail; + } + + ss = c->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &ss) < 0) { + pa_log(__FILE__": failed to parse sample specification\n"); + goto fail; + } + + if (ss_to_waveformat(&ss, &wf) < 0) + goto fail; + + u = pa_xmalloc(sizeof(struct userdata)); + + if (record) { + if (waveInOpen(&hwi, WAVE_MAPPER, &wf, (DWORD_PTR)chunk_ready_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) + goto fail; + if (waveInStart(hwi) != MMSYSERR_NOERROR) + goto fail; + pa_log_debug(__FILE__": Opened waveIn subsystem.\n"); + } + + if (playback) { + if (waveOutOpen(&hwo, WAVE_MAPPER, &wf, (DWORD_PTR)chunk_done_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) + goto fail; + pa_log_debug(__FILE__": Opened waveOut subsystem.\n"); + } + + InitializeCriticalSection(&u->crit); + + if (hwi != INVALID_HANDLE_VALUE) { + u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, NULL); + assert(u->source); + u->source->userdata = u; + u->source->notify = notify_source_cb; + u->source->get_latency = source_get_latency_cb; + pa_source_set_owner(u->source, m); + u->source->description = pa_sprintf_malloc("Windows waveIn PCM"); + } else + u->source = NULL; + + if (hwo != INVALID_HANDLE_VALUE) { + u->sink = pa_sink_new(c, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL); + assert(u->sink); + u->sink->notify = notify_sink_cb; + u->sink->get_latency = sink_get_latency_cb; + u->sink->userdata = u; + pa_sink_set_owner(u->sink, m); + u->sink->description = pa_sprintf_malloc("Windows waveOut PCM"); + } else + u->sink = NULL; + + assert(u->source || u->sink); + + u->core = c; + u->hwi = hwi; + u->hwo = hwo; + + u->fragments = nfrags; + u->free_ifrags = u->fragments; + u->free_ofrags = u->fragments; + u->fragment_size = frag_size - (frag_size % pa_frame_size(&ss)); + + u->written_bytes = 0; + + u->oremain = u->fragment_size; + + u->poll_timeout = pa_bytes_to_usec(u->fragments * u->fragment_size / 3, &ss); + + pa_gettimeofday(&tv); + pa_timeval_add(&tv, u->poll_timeout); + + u->event = c->mainloop->time_new(c->mainloop, &tv, poll_cb, u); + assert(u->event); + + u->defer = c->mainloop->defer_new(c->mainloop, defer_cb, u); + assert(u->defer); + c->mainloop->defer_enable(u->defer, 0); + + u->cur_ihdr = 0; + u->cur_ohdr = 0; + u->ihdrs = pa_xmalloc0(sizeof(WAVEHDR) * u->fragments); + assert(u->ihdrs); + u->ohdrs = pa_xmalloc0(sizeof(WAVEHDR) * u->fragments); + assert(u->ohdrs); + for (i = 0;i < u->fragments;i++) { + u->ihdrs[i].dwBufferLength = u->fragment_size; + u->ohdrs[i].dwBufferLength = u->fragment_size; + u->ihdrs[i].lpData = pa_xmalloc(u->fragment_size); + assert(u->ihdrs); + u->ohdrs[i].lpData = pa_xmalloc(u->fragment_size); + assert(u->ohdrs); + } + + u->silence.length = u->fragment_size; + u->silence.memblock = pa_memblock_new(u->silence.length, u->core->memblock_stat); + assert(u->silence.memblock); + pa_silence_memblock(u->silence.memblock, &ss); + u->silence.index = 0; + + u->module = m; + m->userdata = u; + + pa_modargs_free(ma); + + return 0; + +fail: + if (hwi != INVALID_HANDLE_VALUE) + waveInClose(hwi); + + if (hwo != INVALID_HANDLE_VALUE) + waveOutClose(hwo); + + if (u) + pa_xfree(u); + + if (ma) + pa_modargs_free(ma); + + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata *u; + unsigned int i; + + assert(c && m); + + if (!(u = m->userdata)) + return; + + if (u->event) + c->mainloop->time_free(u->event); + + if (u->defer) + c->mainloop->defer_free(u->defer); + + if (u->sink) { + pa_sink_disconnect(u->sink); + pa_sink_unref(u->sink); + } + + if (u->source) { + pa_source_disconnect(u->source); + pa_source_unref(u->source); + } + + if (u->hwi != INVALID_HANDLE_VALUE) { + waveInReset(u->hwi); + waveInClose(u->hwi); + } + + if (u->hwo != INVALID_HANDLE_VALUE) { + waveOutReset(u->hwo); + waveOutClose(u->hwo); + } + + for (i = 0;i < u->fragments;i++) { + pa_xfree(u->ihdrs[i].lpData); + pa_xfree(u->ohdrs[i].lpData); + } + + pa_xfree(u->ihdrs); + pa_xfree(u->ohdrs); + + DeleteCriticalSection(&u->crit); + + pa_xfree(u); +} diff --git a/src/modules/module-x11-bell.c b/src/modules/module-x11-bell.c new file mode 100644 index 00000000..d722b732 --- /dev/null +++ b/src/modules/module-x11-bell.c @@ -0,0 +1,173 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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= sample= display=") + +struct userdata { + pa_core *core; + int xkb_event_base; + char *sink_name; + char *scache_item; + Display *display; + + pa_x11_wrapper *x11_wrapper; + pa_x11_client *x11_client; +}; + +static const char* const valid_modargs[] = { + "sink", + "sample", + "display", + NULL +}; + +static int ring_bell(struct userdata *u, int percent) { + pa_sink *s; + pa_cvolume cv; + assert(u); + + if (!(s = pa_namereg_get(u->core, u->sink_name, PA_NAMEREG_SINK, 1))) { + pa_log(__FILE__": Invalid sink: %s\n", u->sink_name); + return -1; + } + + pa_scache_play_item(u->core, u->scache_item, s, pa_cvolume_set(&cv, PA_CHANNELS_MAX, percent*PA_VOLUME_NORM/100)); + return 0; +} + +static int x11_event_callback(pa_x11_wrapper *w, XEvent *e, void *userdata) { + XkbBellNotifyEvent *bne; + struct userdata *u = userdata; + assert(w && e && u && u->x11_wrapper == w); + + if (((XkbEvent*) e)->any.xkb_type != XkbBellNotify) + return 0; + + bne = (XkbBellNotifyEvent*) e; + + if (ring_bell(u, bne->percent) < 0) { + pa_log_info(__FILE__": Ringing bell failed, reverting to X11 device bell.\n"); + XkbForceDeviceBell(pa_x11_wrapper_get_display(w), bne->device, bne->bell_class, bne->bell_id, bne->percent); + } + + return 1; +} + +int pa__init(pa_core *c, pa_module*m) { + struct userdata *u = NULL; + pa_modargs *ma = NULL; + int major, minor; + unsigned int auto_ctrls, auto_values; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments\n"); + goto fail; + } + + m->userdata = u = pa_xmalloc(sizeof(struct userdata)); + u->core = c; + u->scache_item = pa_xstrdup(pa_modargs_get_value(ma, "sample", "x11-bell")); + u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); + u->x11_client = NULL; + + if (!(u->x11_wrapper = pa_x11_wrapper_get(c, pa_modargs_get_value(ma, "display", NULL)))) + goto fail; + + u->display = pa_x11_wrapper_get_display(u->x11_wrapper); + + major = XkbMajorVersion; + minor = XkbMinorVersion; + + if (!XkbLibraryVersion(&major, &minor)) { + pa_log(__FILE__": XkbLibraryVersion() failed\n"); + goto fail; + } + + major = XkbMajorVersion; + minor = XkbMinorVersion; + + + if (!XkbQueryExtension(u->display, NULL, &u->xkb_event_base, NULL, &major, &minor)) { + pa_log(__FILE__": XkbQueryExtension() failed\n"); + goto fail; + } + + XkbSelectEvents(u->display, XkbUseCoreKbd, XkbBellNotifyMask, XkbBellNotifyMask); + auto_ctrls = auto_values = XkbAudibleBellMask; + XkbSetAutoResetControls(u->display, XkbAudibleBellMask, &auto_ctrls, &auto_values); + XkbChangeEnabledControls(u->display, XkbUseCoreKbd, XkbAudibleBellMask, 0); + + u->x11_client = pa_x11_client_new(u->x11_wrapper, x11_event_callback, u); + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + if (m->userdata) + pa__done(c, m); + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata *u = m->userdata; + assert(c && m && u); + + pa_xfree(u->scache_item); + pa_xfree(u->sink_name); + + if (u->x11_client) + pa_x11_client_free(u->x11_client); + + if (u->x11_wrapper) + pa_x11_wrapper_unref(u->x11_wrapper); + + pa_xfree(u); +} diff --git a/src/modules/module-x11-publish.c b/src/modules/module-x11-publish.c new file mode 100644 index 00000000..dca5d049 --- /dev/null +++ b/src/modules/module-x11-publish.c @@ -0,0 +1,192 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "module-x11-publish-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("X11 Credential Publisher") +PA_MODULE_VERSION(PACKAGE_VERSION) +PA_MODULE_USAGE("display=") + +static const char* const valid_modargs[] = { + "display", + "sink", + "source", + "cookie", + NULL +}; + +struct userdata { + pa_core *core; + pa_x11_wrapper *x11_wrapper; + Display *display; + char *id; + uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH]; + int auth_cookie_in_property; +}; + +static int load_key(struct userdata *u, const char*fn) { + assert(u); + + u->auth_cookie_in_property = 0; + + if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) { + pa_log_debug(__FILE__": using already loaded auth cookie.\n"); + pa_authkey_prop_ref(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME); + u->auth_cookie_in_property = 1; + return 0; + } + + if (!fn) + fn = PA_NATIVE_COOKIE_FILE; + + if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0) + return -1; + + pa_log_debug(__FILE__": loading cookie from disk.\n"); + + if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) + u->auth_cookie_in_property = 1; + + return 0; +} + +int pa__init(pa_core *c, pa_module*m) { + struct userdata *u; + pa_modargs *ma = NULL; + char hn[256], un[128]; + char hx[PA_NATIVE_COOKIE_LENGTH*2+1]; + const char *t; + char *s; + pa_strlist *l; + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments\n"); + goto fail; + } + + m->userdata = u = pa_xmalloc(sizeof(struct userdata)); + u->core = c; + u->id = NULL; + u->auth_cookie_in_property = 0; + + if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0) + goto fail; + + if (!(u->x11_wrapper = pa_x11_wrapper_get(c, pa_modargs_get_value(ma, "display", NULL)))) + goto fail; + + u->display = pa_x11_wrapper_get_display(u->x11_wrapper); + + if (!(l = pa_property_get(c, PA_NATIVE_SERVER_PROPERTY_NAME))) + goto fail; + + s = pa_strlist_tostring(l); + pa_x11_set_prop(u->display, "POLYP_SERVER", s); + pa_xfree(s); + + if (!pa_get_fqdn(hn, sizeof(hn)) || !pa_get_user_name(un, sizeof(un))) + goto fail; + + u->id = pa_sprintf_malloc("%s@%s/%u", un, hn, (unsigned) getpid()); + pa_x11_set_prop(u->display, "POLYP_ID", u->id); + + if ((t = pa_modargs_get_value(ma, "source", NULL))) + pa_x11_set_prop(u->display, "POLYP_SOURCE", t); + + if ((t = pa_modargs_get_value(ma, "sink", NULL))) + pa_x11_set_prop(u->display, "POLYP_SINK", t); + + pa_x11_set_prop(u->display, "POLYP_COOKIE", pa_hexstr(u->auth_cookie, sizeof(u->auth_cookie), hx, sizeof(hx))); + + pa_modargs_free(ma); + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(c, m); + return -1; +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata*u; + assert(c && m); + + if (!(u = m->userdata)) + return; + + if (u->x11_wrapper) { + char t[256]; + + /* Yes, here is a race condition */ + if (!pa_x11_get_prop(u->display, "POLYP_ID", t, sizeof(t)) || strcmp(t, u->id)) + pa_log("WARNING: Polypaudio information vanished from X11!\n"); + else { + pa_x11_del_prop(u->display, "POLYP_ID"); + pa_x11_del_prop(u->display, "POLYP_SERVER"); + pa_x11_del_prop(u->display, "POLYP_SINK"); + pa_x11_del_prop(u->display, "POLYP_SOURCE"); + pa_x11_del_prop(u->display, "POLYP_COOKIE"); + XSync(u->display, False); + } + } + + if (u->x11_wrapper) + pa_x11_wrapper_unref(u->x11_wrapper); + + if (u->auth_cookie_in_property) + pa_authkey_prop_unref(c, PA_NATIVE_COOKIE_PROPERTY_NAME); + + pa_xfree(u->id); + pa_xfree(u); +} + diff --git a/src/modules/module-zeroconf-publish.c b/src/modules/module-zeroconf-publish.c new file mode 100644 index 00000000..45d566ae --- /dev/null +++ b/src/modules/module-zeroconf-publish.c @@ -0,0 +1,508 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "module-zeroconf-publish-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Publisher") +PA_MODULE_VERSION(PACKAGE_VERSION) +PA_MODULE_USAGE("port=") + +#define SERVICE_NAME_SINK "_polypaudio-sink._tcp" +#define SERVICE_NAME_SOURCE "_polypaudio-source._tcp" +#define SERVICE_NAME_SERVER "_polypaudio-server._tcp" + +static const char* const valid_modargs[] = { + "port", + NULL +}; + +struct service { + sw_discovery_oid oid; + char *name; + int published; /* 0 -> not yet registered, 1 -> registered with data from real device, 2 -> registered with data from autoload device */ + + struct { + int valid; + pa_namereg_type type; + uint32_t index; + } loaded; + + struct { + int valid; + pa_namereg_type type; + uint32_t index; + } autoload; +}; + +struct userdata { + pa_core *core; + pa_howl_wrapper *howl_wrapper; + pa_hashmap *services; + pa_dynarray *sink_dynarray, *source_dynarray, *autoload_dynarray; + pa_subscription *subscription; + + uint16_t port; + sw_discovery_oid server_oid; +}; + +static sw_result publish_reply(sw_discovery discovery, sw_discovery_publish_status status, sw_discovery_oid oid, sw_opaque extra) { + return SW_OKAY; +} + +static void get_service_data(struct userdata *u, struct service *s, pa_sample_spec *ret_ss, char **ret_description, pa_typeid_t *ret_typeid) { + assert(u && s && s->loaded.valid && ret_ss && ret_description && ret_typeid); + + if (s->loaded.type == PA_NAMEREG_SINK) { + pa_sink *sink = pa_idxset_get_by_index(u->core->sinks, s->loaded.index); + assert(sink); + *ret_ss = sink->sample_spec; + *ret_description = sink->description; + *ret_typeid = sink->typeid; + } else if (s->loaded.type == PA_NAMEREG_SOURCE) { + pa_source *source = pa_idxset_get_by_index(u->core->sources, s->loaded.index); + assert(source); + *ret_ss = source->sample_spec; + *ret_description = source->description; + *ret_typeid = source->typeid; + } else + assert(0); +} + +static void txt_record_server_data(pa_core *c, sw_text_record t) { + char s[256]; + assert(c); + + sw_text_record_add_key_and_string_value(t, "server-version", PACKAGE_NAME" "PACKAGE_VERSION); + sw_text_record_add_key_and_string_value(t, "user-name", pa_get_user_name(s, sizeof(s))); + sw_text_record_add_key_and_string_value(t, "fqdn", pa_get_fqdn(s, sizeof(s))); + snprintf(s, sizeof(s), "0x%08x", c->cookie); + sw_text_record_add_key_and_string_value(t, "cookie", s); +} + +static int publish_service(struct userdata *u, struct service *s) { + char t[256]; + char hn[256]; + int r = -1; + sw_text_record txt; + int free_txt = 0; + assert(u && s); + + if ((s->published == 1 && s->loaded.valid) || + (s->published == 2 && s->autoload.valid && !s->loaded.valid)) + return 0; + + if (s->published) { + sw_discovery_cancel(pa_howl_wrapper_get_discovery(u->howl_wrapper), s->oid); + s->published = 0; + } + + snprintf(t, sizeof(t), "Networked Audio Device %s on %s", s->name, pa_get_host_name(hn, sizeof(hn))); + + if (sw_text_record_init(&txt) != SW_OKAY) { + pa_log(__FILE__": sw_text_record_init() failed\n"); + goto finish; + } + free_txt = 1; + + sw_text_record_add_key_and_string_value(txt, "device", s->name); + + txt_record_server_data(u->core, txt); + + if (s->loaded.valid) { + char z[64], *description; + pa_typeid_t typeid; + pa_sample_spec ss; + + get_service_data(u, s, &ss, &description, &typeid); + + snprintf(z, sizeof(z), "%u", ss.rate); + sw_text_record_add_key_and_string_value(txt, "rate", z); + snprintf(z, sizeof(z), "%u", ss.channels); + sw_text_record_add_key_and_string_value(txt, "channels", z); + sw_text_record_add_key_and_string_value(txt, "format", pa_sample_format_to_string(ss.format)); + + sw_text_record_add_key_and_string_value(txt, "description", description); + + snprintf(z, sizeof(z), "0x%8x", typeid); + sw_text_record_add_key_and_string_value(txt, "typeid", z); + + + if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t, + s->loaded.type == PA_NAMEREG_SINK ? SERVICE_NAME_SINK : SERVICE_NAME_SOURCE, + NULL, NULL, u->port, sw_text_record_bytes(txt), sw_text_record_len(txt), + publish_reply, s, &s->oid) != SW_OKAY) { + pa_log(__FILE__": failed to register sink on zeroconf.\n"); + goto finish; + } + + s->published = 1; + } else if (s->autoload.valid) { + + if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t, + s->autoload.type == PA_NAMEREG_SINK ? SERVICE_NAME_SINK : SERVICE_NAME_SOURCE, + NULL, NULL, u->port, sw_text_record_bytes(txt), sw_text_record_len(txt), + publish_reply, s, &s->oid) != SW_OKAY) { + pa_log(__FILE__": failed to register sink on zeroconf.\n"); + goto finish; + } + + s->published = 2; + } + + r = 0; + +finish: + + if (!s->published) { + /* Remove this service */ + pa_hashmap_remove(u->services, s->name); + pa_xfree(s->name); + pa_xfree(s); + } + + if (free_txt) + sw_text_record_fina(txt); + + return r; +} + +struct service *get_service(struct userdata *u, const char *name) { + struct service *s; + + if ((s = pa_hashmap_get(u->services, name))) + return s; + + s = pa_xmalloc(sizeof(struct service)); + s->published = 0; + s->name = pa_xstrdup(name); + s->loaded.valid = s->autoload.valid = 0; + + pa_hashmap_put(u->services, s->name, s); + + return s; +} + +static int publish_sink(struct userdata *u, pa_sink *s) { + struct service *svc; + assert(u && s); + + svc = get_service(u, s->name); + if (svc->loaded.valid) + return 0; + + svc->loaded.valid = 1; + svc->loaded.type = PA_NAMEREG_SINK; + svc->loaded.index = s->index; + + pa_dynarray_put(u->sink_dynarray, s->index, svc); + + return publish_service(u, svc); +} + +static int publish_source(struct userdata *u, pa_source *s) { + struct service *svc; + assert(u && s); + + svc = get_service(u, s->name); + if (svc->loaded.valid) + return 0; + + svc->loaded.valid = 1; + svc->loaded.type = PA_NAMEREG_SOURCE; + svc->loaded.index = s->index; + + pa_dynarray_put(u->source_dynarray, s->index, svc); + + return publish_service(u, svc); +} + +static int publish_autoload(struct userdata *u, pa_autoload_entry *s) { + struct service *svc; + assert(u && s); + + svc = get_service(u, s->name); + if (svc->autoload.valid) + return 0; + + svc->autoload.valid = 1; + svc->autoload.type = s->type; + svc->autoload.index = s->index; + + pa_dynarray_put(u->autoload_dynarray, s->index, svc); + + return publish_service(u, svc); +} + +static int remove_sink(struct userdata *u, uint32_t index) { + struct service *svc; + assert(u && index != PA_INVALID_INDEX); + + if (!(svc = pa_dynarray_get(u->sink_dynarray, index))) + return 0; + + if (!svc->loaded.valid || svc->loaded.type != PA_NAMEREG_SINK) + return 0; + + svc->loaded.valid = 0; + pa_dynarray_put(u->sink_dynarray, index, NULL); + + return publish_service(u, svc); +} + +static int remove_source(struct userdata *u, uint32_t index) { + struct service *svc; + assert(u && index != PA_INVALID_INDEX); + + if (!(svc = pa_dynarray_get(u->source_dynarray, index))) + return 0; + + if (!svc->loaded.valid || svc->loaded.type != PA_NAMEREG_SOURCE) + return 0; + + svc->loaded.valid = 0; + pa_dynarray_put(u->source_dynarray, index, NULL); + + return publish_service(u, svc); +} + +static int remove_autoload(struct userdata *u, uint32_t index) { + struct service *svc; + assert(u && index != PA_INVALID_INDEX); + + if (!(svc = pa_dynarray_get(u->autoload_dynarray, index))) + return 0; + + if (!svc->autoload.valid) + return 0; + + svc->autoload.valid = 0; + pa_dynarray_put(u->autoload_dynarray, index, NULL); + + return publish_service(u, svc); +} + +static void subscribe_callback(pa_core *c, pa_subscription_event_type t, uint32_t index, void *userdata) { + struct userdata *u = userdata; + assert(u && c); + + switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) + case PA_SUBSCRIPTION_EVENT_SINK: { + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + pa_sink *sink; + + if ((sink = pa_idxset_get_by_index(c->sinks, index))) { + if (publish_sink(u, sink) < 0) + goto fail; + } + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + if (remove_sink(u, index) < 0) + goto fail; + } + + break; + + case PA_SUBSCRIPTION_EVENT_SOURCE: + + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + pa_source *source; + + if ((source = pa_idxset_get_by_index(c->sources, index))) { + if (publish_source(u, source) < 0) + goto fail; + } + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + if (remove_source(u, index) < 0) + goto fail; + } + + break; + + case PA_SUBSCRIPTION_EVENT_AUTOLOAD: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + pa_autoload_entry *autoload; + + if ((autoload = pa_idxset_get_by_index(c->autoload_idxset, index))) { + if (publish_autoload(u, autoload) < 0) + goto fail; + } + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + if (remove_autoload(u, index) < 0) + goto fail; + } + + break; + } + + return; + +fail: + if (u->subscription) { + pa_subscription_free(u->subscription); + u->subscription = NULL; + } +} + +int pa__init(pa_core *c, pa_module*m) { + struct userdata *u; + uint32_t index, port = PA_NATIVE_DEFAULT_PORT; + pa_sink *sink; + pa_source *source; + pa_autoload_entry *autoload; + pa_modargs *ma = NULL; + char t[256], hn[256]; + int free_txt = 0; + sw_text_record txt; + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments.\n"); + goto fail; + } + + if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port == 0 || port >= 0xFFFF) { + pa_log(__FILE__": invalid port specified.\n"); + goto fail; + } + + m->userdata = u = pa_xmalloc(sizeof(struct userdata)); + u->core = c; + u->port = (uint16_t) port; + + if (!(u->howl_wrapper = pa_howl_wrapper_get(c))) + goto fail; + + u->services = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + u->sink_dynarray = pa_dynarray_new(); + u->source_dynarray = pa_dynarray_new(); + u->autoload_dynarray = pa_dynarray_new(); + + u->subscription = pa_subscription_new(c, + PA_SUBSCRIPTION_MASK_SINK| + PA_SUBSCRIPTION_MASK_SOURCE| + PA_SUBSCRIPTION_MASK_AUTOLOAD, subscribe_callback, u); + + for (sink = pa_idxset_first(c->sinks, &index); sink; sink = pa_idxset_next(c->sinks, &index)) + if (publish_sink(u, sink) < 0) + goto fail; + + for (source = pa_idxset_first(c->sources, &index); source; source = pa_idxset_next(c->sources, &index)) + if (publish_source(u, source) < 0) + goto fail; + + if (c->autoload_idxset) + for (autoload = pa_idxset_first(c->autoload_idxset, &index); autoload; autoload = pa_idxset_next(c->autoload_idxset, &index)) + if (publish_autoload(u, autoload) < 0) + goto fail; + + snprintf(t, sizeof(t), "Networked Audio Server on %s", pa_get_host_name(hn, sizeof(hn))); + + if (sw_text_record_init(&txt) != SW_OKAY) { + pa_log(__FILE__": sw_text_record_init() failed\n"); + goto fail; + } + free_txt = 1; + + txt_record_server_data(u->core, txt); + + if (sw_discovery_publish(pa_howl_wrapper_get_discovery(u->howl_wrapper), 0, t, + SERVICE_NAME_SERVER, + NULL, NULL, u->port, sw_text_record_bytes(txt), sw_text_record_len(txt), + publish_reply, u, &u->server_oid) != SW_OKAY) { + pa_log(__FILE__": failed to register server on zeroconf.\n"); + goto fail; + } + + sw_text_record_fina(txt); + pa_modargs_free(ma); + + return 0; + +fail: + pa__done(c, m); + + if (ma) + pa_modargs_free(ma); + + if (free_txt) + sw_text_record_fina(txt); + + return -1; +} + +static void service_free(void *p, void *userdata) { + struct service *s = p; + struct userdata *u = userdata; + assert(s && u); + sw_discovery_cancel(pa_howl_wrapper_get_discovery(u->howl_wrapper), s->oid); + pa_xfree(s->name); + pa_xfree(s); +} + +void pa__done(pa_core *c, pa_module*m) { + struct userdata*u; + assert(c && m); + + if (!(u = m->userdata)) + return; + + if (u->services) + pa_hashmap_free(u->services, service_free, u); + + if (u->sink_dynarray) + pa_dynarray_free(u->sink_dynarray, NULL, NULL); + if (u->source_dynarray) + pa_dynarray_free(u->source_dynarray, NULL, NULL); + if (u->autoload_dynarray) + pa_dynarray_free(u->autoload_dynarray, NULL, NULL); + + if (u->subscription) + pa_subscription_free(u->subscription); + + if (u->howl_wrapper) + pa_howl_wrapper_unref(u->howl_wrapper); + + + pa_xfree(u); +} + diff --git a/src/polyp/cdecl.h b/src/polyp/cdecl.h new file mode 100644 index 00000000..d51ae026 --- /dev/null +++ b/src/polyp/cdecl.h @@ -0,0 +1,42 @@ +#ifndef foocdeclhfoo +#define foocdeclhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/** \file + * C++ compatibility support */ + +#ifdef __cplusplus +/** If using C++ this macro enables C mode, otherwise does nothing */ +#define PA_C_DECL_BEGIN extern "C" { +/** If using C++ this macros switches back to C++ mode, otherwise does nothing */ +#define PA_C_DECL_END } + +#else +/** If using C++ this macro enables C mode, otherwise does nothing */ +#define PA_C_DECL_BEGIN +/** If using C++ this macros switches back to C++ mode, otherwise does nothing */ +#define PA_C_DECL_END + +#endif + +#endif diff --git a/src/polyp/channelmap.c b/src/polyp/channelmap.c new file mode 100644 index 00000000..7bfd21e6 --- /dev/null +++ b/src/polyp/channelmap.c @@ -0,0 +1,202 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "channelmap.h" + +pa_channel_map* pa_channel_map_init(pa_channel_map *m) { + unsigned c; + assert(m); + + m->channels = 0; + + for (c = 0; c < PA_CHANNELS_MAX; c++) + m->map[c] = PA_CHANNEL_POSITION_INVALID; + + return m; +} + +pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) { + assert(m); + + pa_channel_map_init(m); + + m->channels = 1; + m->map[0] = PA_CHANNEL_POSITION_MONO; + return m; +} + +pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) { + assert(m); + + pa_channel_map_init(m); + + m->channels = 2; + m->map[0] = PA_CHANNEL_POSITION_LEFT; + m->map[1] = PA_CHANNEL_POSITION_RIGHT; + return m; +} + +pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels) { + assert(m); + assert(channels > 0); + assert(channels <= PA_CHANNELS_MAX); + + pa_channel_map_init(m); + + m->channels = channels; + + switch (channels) { + case 1: + m->map[0] = PA_CHANNEL_POSITION_MONO; + return m; + + case 8: + m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT; + m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT; + /* Fall through */ + + case 6: + m->map[5] = PA_CHANNEL_POSITION_LFE; + /* Fall through */ + + case 5: + m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; + /* Fall through */ + + case 4: + m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT; + m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; + /* Fall through */ + + case 2: + m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; + m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; + return m; + + default: + return NULL; + } +} + +const char* pa_channel_position_to_string(pa_channel_position_t pos) { + + const char *const table[] = { + [PA_CHANNEL_POSITION_MONO] = "mono", + + [PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center", + [PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left", + [PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right", + + [PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center", + [PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left", + [PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right", + + [PA_CHANNEL_POSITION_LFE] = "lfe", + + [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center", + [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center", + + [PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left", + [PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right", + + [PA_CHANNEL_POSITION_AUX1] = "aux1", + [PA_CHANNEL_POSITION_AUX2] = "aux2", + [PA_CHANNEL_POSITION_AUX3] = "aux3", + [PA_CHANNEL_POSITION_AUX4] = "aux4", + [PA_CHANNEL_POSITION_AUX5] = "aux5", + [PA_CHANNEL_POSITION_AUX6] = "aux6", + [PA_CHANNEL_POSITION_AUX7] = "aux7", + [PA_CHANNEL_POSITION_AUX8] = "aux8", + [PA_CHANNEL_POSITION_AUX9] = "aux9", + [PA_CHANNEL_POSITION_AUX10] = "aux10", + [PA_CHANNEL_POSITION_AUX11] = "aux11", + [PA_CHANNEL_POSITION_AUX12] = "aux12" + }; + + if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX) + return NULL; + + return table[pos]; +} + +int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) { + unsigned c; + + assert(a); + assert(b); + + if (a->channels != b->channels) + return 0; + + for (c = 0; c < a->channels; c++) + if (a->map[c] != b->map[c]) + return 0; + + return 1; +} + +char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) { + unsigned channel; + int first = 1; + char *e; + + assert(s); + assert(l > 0); + assert(map); + + *(e = s) = 0; + + for (channel = 0; channel < map->channels && l > 1; channel++) { + l -= snprintf(e, l, "%s%u:%s", + first ? "" : " ", + channel, + pa_channel_position_to_string(map->map[channel])); + + e = strchr(e, 0); + first = 0; + } + + return s; +} + +int pa_channel_map_valid(const pa_channel_map *map) { + unsigned c; + + assert(map); + + if (map->channels <= 0 || map->channels > PA_CHANNELS_MAX) + return 0; + + for (c = 0; c < map->channels; c++) + if (map->map[c] < 0 ||map->map[c] >= PA_CHANNEL_POSITION_MAX) + return 0; + + return 1; +} diff --git a/src/polyp/channelmap.h b/src/polyp/channelmap.h new file mode 100644 index 00000000..0b9f6e26 --- /dev/null +++ b/src/polyp/channelmap.h @@ -0,0 +1,98 @@ +#ifndef foochannelmaphfoo +#define foochannelmaphfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +/** \file + * Constants and routines for channel mapping handling */ + +PA_C_DECL_BEGIN + +typedef enum { + PA_CHANNEL_POSITION_INVALID = -1, + PA_CHANNEL_POSITION_MONO = 0, + + PA_CHANNEL_POSITION_LEFT, + PA_CHANNEL_POSITION_RIGHT, + + PA_CHANNEL_POSITION_FRONT_CENTER, + PA_CHANNEL_POSITION_FRONT_LEFT = PA_CHANNEL_POSITION_LEFT, + PA_CHANNEL_POSITION_FRONT_RIGHT = PA_CHANNEL_POSITION_RIGHT, + + PA_CHANNEL_POSITION_REAR_CENTER, + PA_CHANNEL_POSITION_REAR_LEFT, + PA_CHANNEL_POSITION_REAR_RIGHT, + + PA_CHANNEL_POSITION_LFE, + PA_CHANNEL_POSITION_SUBWOOFER = PA_CHANNEL_POSITION_LFE, + + PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, + PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, + + PA_CHANNEL_POSITION_SIDE_LEFT, + PA_CHANNEL_POSITION_SIDE_RIGHT, + + PA_CHANNEL_POSITION_AUX0, + PA_CHANNEL_POSITION_AUX1, + PA_CHANNEL_POSITION_AUX2, + PA_CHANNEL_POSITION_AUX3, + PA_CHANNEL_POSITION_AUX4, + PA_CHANNEL_POSITION_AUX5, + PA_CHANNEL_POSITION_AUX6, + PA_CHANNEL_POSITION_AUX7, + PA_CHANNEL_POSITION_AUX8, + PA_CHANNEL_POSITION_AUX9, + PA_CHANNEL_POSITION_AUX10, + PA_CHANNEL_POSITION_AUX11, + PA_CHANNEL_POSITION_AUX12, + PA_CHANNEL_POSITION_AUX13, + PA_CHANNEL_POSITION_AUX14, + PA_CHANNEL_POSITION_AUX15, + + PA_CHANNEL_POSITION_MAX +} pa_channel_position_t; + +typedef struct pa_channel_map { + uint8_t channels; + pa_channel_position_t map[PA_CHANNELS_MAX]; +} pa_channel_map; + +pa_channel_map* pa_channel_map_init(pa_channel_map *m); +pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m); +pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m); +pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels); + +const char* pa_channel_position_to_string(pa_channel_position_t pos); + +#define PA_CHANNEL_MAP_SNPRINT_MAX 64 +char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map); + +int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b); + +int pa_channel_map_valid(const pa_channel_map *map); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/client-conf-x11.c b/src/polyp/client-conf-x11.c new file mode 100644 index 00000000..83d0bd2e --- /dev/null +++ b/src/polyp/client-conf-x11.c @@ -0,0 +1,91 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-13071 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include + +#include "client-conf-x11.h" +#include +#include +#include +#include + +int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) { + Display *d = NULL; + int ret = -1; + char t[1024]; + + if (!dname && !getenv("DISPLAY")) + goto finish; + + if (!(d = XOpenDisplay(dname))) { + pa_log(__FILE__": XOpenDisplay() failed\n"); + goto finish; + } + + if (pa_x11_get_prop(d, "POLYP_SERVER", t, sizeof(t))) { + pa_xfree(c->default_server); + c->default_server = pa_xstrdup(t); + } + + if (pa_x11_get_prop(d, "POLYP_SINK", t, sizeof(t))) { + pa_xfree(c->default_sink); + c->default_sink = pa_xstrdup(t); + } + + if (pa_x11_get_prop(d, "POLYP_SOURCE", t, sizeof(t))) { + pa_xfree(c->default_source); + c->default_source = pa_xstrdup(t); + } + + if (pa_x11_get_prop(d, "POLYP_COOKIE", t, sizeof(t))) { + uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; + + if (pa_parsehex(t, cookie, sizeof(cookie)) != sizeof(cookie)) { + pa_log(__FILE__": failed to parse cookie data\n"); + goto finish; + } + + assert(sizeof(cookie) == sizeof(c->cookie)); + memcpy(c->cookie, cookie, sizeof(cookie)); + + c->cookie_valid = 1; + + pa_xfree(c->cookie_file); + c->cookie_file = NULL; + } + + ret = 0; + +finish: + if (d) + XCloseDisplay(d); + + return ret; + +} diff --git a/src/polyp/client-conf-x11.h b/src/polyp/client-conf-x11.h new file mode 100644 index 00000000..80841171 --- /dev/null +++ b/src/polyp/client-conf-x11.h @@ -0,0 +1,31 @@ +#ifndef fooclientconfx11hfoo +#define fooclientconfx11hfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +/* Load client configuration data from the specified X11 display, + * overwriting the current settings in *c */ +int pa_client_conf_from_x11(pa_client_conf *c, const char *display); + +#endif diff --git a/src/polyp/client-conf.c b/src/polyp/client-conf.c new file mode 100644 index 00000000..2df201ce --- /dev/null +++ b/src/polyp/client-conf.c @@ -0,0 +1,191 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#ifndef DEFAULT_CONFIG_DIR +# ifndef OS_IS_WIN32 +# define DEFAULT_CONFIG_DIR "/etc/polypaudio" +# else +# define DEFAULT_CONFIG_DIR "%POLYP_ROOT%" +# endif +#endif + +#ifndef OS_IS_WIN32 +# define PATH_SEP "/" +#else +# define PATH_SEP "\\" +#endif + +#define DEFAULT_CLIENT_CONFIG_FILE DEFAULT_CONFIG_DIR PATH_SEP "client.conf" +#define DEFAULT_CLIENT_CONFIG_FILE_USER ".polypaudio" PATH_SEP "client.conf" + +#define ENV_CLIENT_CONFIG_FILE "POLYP_CLIENTCONFIG" +#define ENV_DEFAULT_SINK "POLYP_SINK" +#define ENV_DEFAULT_SOURCE "POLYP_SOURCE" +#define ENV_DEFAULT_SERVER "POLYP_SERVER" +#define ENV_DAEMON_BINARY "POLYP_BINARY" +#define ENV_COOKIE_FILE "POLYP_COOKIE" + +static const pa_client_conf default_conf = { + .daemon_binary = NULL, + .extra_arguments = NULL, + .default_sink = NULL, + .default_source = NULL, + .default_server = NULL, + .autospawn = 0, + .cookie_file = NULL, + .cookie_valid = 0 +}; + +pa_client_conf *pa_client_conf_new(void) { + pa_client_conf *c = pa_xmemdup(&default_conf, sizeof(default_conf)); + + c->daemon_binary = pa_xstrdup(POLYPAUDIO_BINARY); + c->extra_arguments = pa_xstrdup("--log-target=syslog --exit-idle-time=5"); + c->cookie_file = pa_xstrdup(PA_NATIVE_COOKIE_FILE); + + return c; +} + +void pa_client_conf_free(pa_client_conf *c) { + assert(c); + pa_xfree(c->daemon_binary); + pa_xfree(c->extra_arguments); + pa_xfree(c->default_sink); + pa_xfree(c->default_source); + pa_xfree(c->default_server); + pa_xfree(c->cookie_file); + pa_xfree(c); +} +int pa_client_conf_load(pa_client_conf *c, const char *filename) { + FILE *f = NULL; + char *fn = NULL; + int r = -1; + + /* Prepare the configuration parse table */ + pa_config_item table[] = { + { "daemon-binary", pa_config_parse_string, NULL }, + { "extra-arguments", pa_config_parse_string, NULL }, + { "default-sink", pa_config_parse_string, NULL }, + { "default-source", pa_config_parse_string, NULL }, + { "default-server", pa_config_parse_string, NULL }, + { "autospawn", pa_config_parse_bool, NULL }, + { "cookie-file", pa_config_parse_string, NULL }, + { NULL, NULL, NULL }, + }; + + table[0].data = &c->daemon_binary; + table[1].data = &c->extra_arguments; + table[2].data = &c->default_sink; + table[3].data = &c->default_source; + table[4].data = &c->default_server; + table[5].data = &c->autospawn; + table[6].data = &c->cookie_file; + + f = filename ? + fopen((fn = pa_xstrdup(filename)), "r") : + pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn); + + if (!f && errno != EINTR) { + pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno)); + goto finish; + } + + r = f ? pa_config_parse(fn, f, table, NULL) : 0; + + if (!r) + r = pa_client_conf_load_cookie(c); + + +finish: + pa_xfree(fn); + + if (f) + fclose(f); + + return r; +} + +int pa_client_conf_env(pa_client_conf *c) { + char *e; + + if ((e = getenv(ENV_DEFAULT_SINK))) { + pa_xfree(c->default_sink); + c->default_sink = pa_xstrdup(e); + } + + if ((e = getenv(ENV_DEFAULT_SOURCE))) { + pa_xfree(c->default_source); + c->default_source = pa_xstrdup(e); + } + + if ((e = getenv(ENV_DEFAULT_SERVER))) { + pa_xfree(c->default_server); + c->default_server = pa_xstrdup(e); + } + + if ((e = getenv(ENV_DAEMON_BINARY))) { + pa_xfree(c->daemon_binary); + c->daemon_binary = pa_xstrdup(e); + } + + if ((e = getenv(ENV_COOKIE_FILE))) { + pa_xfree(c->cookie_file); + c->cookie_file = pa_xstrdup(e); + + return pa_client_conf_load_cookie(c); + } + + return 0; +} + +int pa_client_conf_load_cookie(pa_client_conf* c) { + assert(c); + + c->cookie_valid = 0; + + if (!c->cookie_file) + return -1; + + if (pa_authkey_load_auto(c->cookie_file, c->cookie, sizeof(c->cookie)) < 0) + return -1; + + c->cookie_valid = 1; + return 0; +} + diff --git a/src/polyp/client-conf.h b/src/polyp/client-conf.h new file mode 100644 index 00000000..2d8a019f --- /dev/null +++ b/src/polyp/client-conf.h @@ -0,0 +1,52 @@ +#ifndef fooclientconfhfoo +#define fooclientconfhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "../polypcore/native-common.h" + +/* A structure containing configuration data for polypaudio clients. */ + +typedef struct pa_client_conf { + char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file; + int autospawn; + uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; + int cookie_valid; /* non-zero, when cookie is valid */ +} pa_client_conf; + +/* Create a new configuration data object and reset it to defaults */ +pa_client_conf *pa_client_conf_new(void); +void pa_client_conf_free(pa_client_conf *c); + +/* Load the configuration data from the speicified file, overwriting + * the current settings in *c. When the filename is NULL, the + * default client configuration file name is used. */ +int pa_client_conf_load(pa_client_conf *c, const char *filename); + +/* Load the configuration data from the environment of the current + process, overwriting the current settings in *c. */ +int pa_client_conf_env(pa_client_conf *c); + +/* Load cookie data from c->cookie_file into c->cookie */ +int pa_client_conf_load_cookie(pa_client_conf* c); + +#endif diff --git a/src/polyp/glib-mainloop.c b/src/polyp/glib-mainloop.c new file mode 100644 index 00000000..962eb574 --- /dev/null +++ b/src/polyp/glib-mainloop.c @@ -0,0 +1,538 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include "glib.h" +#include + +struct pa_io_event { + pa_glib_mainloop *mainloop; + int dead; + GIOChannel *io_channel; + GSource *source; + GIOCondition io_condition; + int fd; + void (*callback) (pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api *m, pa_io_event *e, void *userdata); + pa_io_event *next, *prev; +}; + +struct pa_time_event { + pa_glib_mainloop *mainloop; + int dead; + GSource *source; + struct timeval timeval; + void (*callback) (pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api *m, pa_time_event*e, void *userdata); + pa_time_event *next, *prev; +}; + +struct pa_defer_event { + pa_glib_mainloop *mainloop; + int dead; + GSource *source; + void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api *m, pa_defer_event*e, void *userdata); + pa_defer_event *next, *prev; +}; + +struct pa_glib_mainloop { + GMainContext *glib_main_context; + pa_mainloop_api api; + GSource *cleanup_source; + pa_io_event *io_events, *dead_io_events; + pa_time_event *time_events, *dead_time_events; + pa_defer_event *defer_events, *dead_defer_events; +}; + +static void schedule_free_dead_events(pa_glib_mainloop *g); + +static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f); + +static pa_io_event* glib_io_new(pa_mainloop_api*m, int fd, pa_io_event_flags_t f, void (*callback) (pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata), void *userdata) { + pa_io_event *e; + pa_glib_mainloop *g; + + assert(m && m->userdata && fd >= 0 && callback); + g = m->userdata; + + e = pa_xmalloc(sizeof(pa_io_event)); + e->mainloop = m->userdata; + e->dead = 0; + e->fd = fd; + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + + e->io_channel = g_io_channel_unix_new(e->fd); + assert(e->io_channel); + e->source = NULL; + e->io_condition = 0; + + glib_io_enable(e, f); + + e->next = g->io_events; + if (e->next) e->next->prev = e; + g->io_events = e; + e->prev = NULL; + + return e; +} + +/* The callback GLIB calls whenever an IO condition is met */ +static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) { + pa_io_event *e = data; + pa_io_event_flags_t f; + assert(source && e && e->io_channel == source); + + f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) | + (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) | + (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) | + (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0); + + e->callback(&e->mainloop->api, e, e->fd, f, e->userdata); + return TRUE; +} + +static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) { + GIOCondition c; + assert(e && !e->dead); + + c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0); + + if (c == e->io_condition) + return; + + if (e->source) { + g_source_destroy(e->source); + g_source_unref(e->source); + } + + e->source = g_io_create_watch(e->io_channel, c | G_IO_ERR | G_IO_HUP); + assert(e->source); + + g_source_set_callback(e->source, (GSourceFunc) io_cb, e, NULL); + g_source_attach(e->source, e->mainloop->glib_main_context); + g_source_set_priority(e->source, G_PRIORITY_DEFAULT); + + e->io_condition = c; +} + +static void glib_io_free(pa_io_event*e) { + assert(e && !e->dead); + + if (e->source) { + g_source_destroy(e->source); + g_source_unref(e->source); + e->source = NULL; + } + + if (e->prev) + e->prev->next = e->next; + else + e->mainloop->io_events = e->next; + + if (e->next) + e->next->prev = e->prev; + + if ((e->next = e->mainloop->dead_io_events)) + e->next->prev = e; + + e->mainloop->dead_io_events = e; + e->prev = NULL; + + e->dead = 1; + schedule_free_dead_events(e->mainloop); +} + +static void glib_io_set_destroy(pa_io_event*e, void (*callback)(pa_mainloop_api*m, pa_io_event *e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* Time sources */ + +static void glib_time_restart(pa_time_event*e, const struct timeval *tv); + +static pa_time_event* glib_time_new(pa_mainloop_api*m, const struct timeval *tv, void (*callback) (pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) { + pa_glib_mainloop *g; + pa_time_event *e; + + assert(m && m->userdata && tv && callback); + g = m->userdata; + + e = pa_xmalloc(sizeof(pa_time_event)); + e->mainloop = g; + e->dead = 0; + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + e->source = NULL; + + glib_time_restart(e, tv); + + e->next = g->time_events; + if (e->next) e->next->prev = e; + g->time_events = e; + e->prev = NULL; + + return e; +} + +static guint msec_diff(const struct timeval *a, const struct timeval *b) { + guint r; + assert(a && b); + + if (a->tv_sec < b->tv_sec) + return 0; + + if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec) + return 0; + + r = (a->tv_sec-b->tv_sec)*1000; + + if (a->tv_usec >= b->tv_usec) + r += (a->tv_usec - b->tv_usec) / 1000; + else + r -= (b->tv_usec - a->tv_usec) / 1000; + + return r; +} + +static gboolean time_cb(gpointer data) { + pa_time_event* e = data; + assert(e && e->mainloop && e->source); + + g_source_unref(e->source); + e->source = NULL; + + e->callback(&e->mainloop->api, e, &e->timeval, e->userdata); + return FALSE; +} + +static void glib_time_restart(pa_time_event*e, const struct timeval *tv) { + struct timeval now; + assert(e && e->mainloop && !e->dead); + + pa_gettimeofday(&now); + if (e->source) { + g_source_destroy(e->source); + g_source_unref(e->source); + } + + if (tv) { + e->timeval = *tv; + e->source = g_timeout_source_new(msec_diff(tv, &now)); + assert(e->source); + g_source_set_callback(e->source, time_cb, e, NULL); + g_source_set_priority(e->source, G_PRIORITY_DEFAULT); + g_source_attach(e->source, e->mainloop->glib_main_context); + } else + e->source = NULL; + } + +static void glib_time_free(pa_time_event *e) { + assert(e && e->mainloop && !e->dead); + + if (e->source) { + g_source_destroy(e->source); + g_source_unref(e->source); + e->source = NULL; + } + + if (e->prev) + e->prev->next = e->next; + else + e->mainloop->time_events = e->next; + + if (e->next) + e->next->prev = e->prev; + + if ((e->next = e->mainloop->dead_time_events)) + e->next->prev = e; + + e->mainloop->dead_time_events = e; + e->prev = NULL; + + e->dead = 1; + schedule_free_dead_events(e->mainloop); +} + +static void glib_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*m, pa_time_event*e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* Deferred sources */ + +static void glib_defer_enable(pa_defer_event *e, int b); + +static pa_defer_event* glib_defer_new(pa_mainloop_api*m, void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata), void *userdata) { + pa_defer_event *e; + pa_glib_mainloop *g; + + assert(m && m->userdata && callback); + g = m->userdata; + + e = pa_xmalloc(sizeof(pa_defer_event)); + e->mainloop = g; + e->dead = 0; + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + e->source = NULL; + + glib_defer_enable(e, 1); + + e->next = g->defer_events; + if (e->next) e->next->prev = e; + g->defer_events = e; + e->prev = NULL; + return e; +} + +static gboolean idle_cb(gpointer data) { + pa_defer_event* e = data; + assert(e && e->mainloop && e->source); + + e->callback(&e->mainloop->api, e, e->userdata); + return TRUE; +} + +static void glib_defer_enable(pa_defer_event *e, int b) { + assert(e && e->mainloop); + + if (e->source && !b) { + g_source_destroy(e->source); + g_source_unref(e->source); + e->source = NULL; + } else if (!e->source && b) { + e->source = g_idle_source_new(); + assert(e->source); + g_source_set_callback(e->source, idle_cb, e, NULL); + g_source_attach(e->source, e->mainloop->glib_main_context); + g_source_set_priority(e->source, G_PRIORITY_HIGH); + } +} + +static void glib_defer_free(pa_defer_event *e) { + assert(e && e->mainloop && !e->dead); + + if (e->source) { + g_source_destroy(e->source); + g_source_unref(e->source); + e->source = NULL; + } + + if (e->prev) + e->prev->next = e->next; + else + e->mainloop->defer_events = e->next; + + if (e->next) + e->next->prev = e->prev; + + if ((e->next = e->mainloop->dead_defer_events)) + e->next->prev = e; + + e->mainloop->dead_defer_events = e; + e->prev = NULL; + + e->dead = 1; + schedule_free_dead_events(e->mainloop); +} + +static void glib_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api *m, pa_defer_event *e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* quit() */ + +static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) { + pa_glib_mainloop *g; + assert(a && a->userdata); + g = a->userdata; + + /* NOOP */ +} + +static const pa_mainloop_api vtable = { + .userdata = NULL, + + .io_new = glib_io_new, + .io_enable = glib_io_enable, + .io_free = glib_io_free, + .io_set_destroy= glib_io_set_destroy, + + .time_new = glib_time_new, + .time_restart = glib_time_restart, + .time_free = glib_time_free, + .time_set_destroy = glib_time_set_destroy, + + .defer_new = glib_defer_new, + .defer_enable = glib_defer_enable, + .defer_free = glib_defer_free, + .defer_set_destroy = glib_defer_set_destroy, + + .quit = glib_quit, +}; + +pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c) { + pa_glib_mainloop *g; + + g = pa_xmalloc(sizeof(pa_glib_mainloop)); + if (c) { + g->glib_main_context = c; + g_main_context_ref(c); + } else + g->glib_main_context = g_main_context_default(); + + g->api = vtable; + g->api.userdata = g; + + g->io_events = g->dead_io_events = NULL; + g->time_events = g->dead_time_events = NULL; + g->defer_events = g->dead_defer_events = NULL; + + g->cleanup_source = NULL; + return g; +} + +static void free_io_events(pa_io_event *e) { + while (e) { + pa_io_event *r = e; + e = r->next; + + if (r->source) { + g_source_destroy(r->source); + g_source_unref(r->source); + } + + if (r->io_channel) + g_io_channel_unref(r->io_channel); + + if (r->destroy_callback) + r->destroy_callback(&r->mainloop->api, r, r->userdata); + + pa_xfree(r); + } +} + +static void free_time_events(pa_time_event *e) { + while (e) { + pa_time_event *r = e; + e = r->next; + + if (r->source) { + g_source_destroy(r->source); + g_source_unref(r->source); + } + + if (r->destroy_callback) + r->destroy_callback(&r->mainloop->api, r, r->userdata); + + pa_xfree(r); + } +} + +static void free_defer_events(pa_defer_event *e) { + while (e) { + pa_defer_event *r = e; + e = r->next; + + if (r->source) { + g_source_destroy(r->source); + g_source_unref(r->source); + } + + if (r->destroy_callback) + r->destroy_callback(&r->mainloop->api, r, r->userdata); + + pa_xfree(r); + } +} + +void pa_glib_mainloop_free(pa_glib_mainloop* g) { + assert(g); + + free_io_events(g->io_events); + free_io_events(g->dead_io_events); + free_defer_events(g->defer_events); + free_defer_events(g->dead_defer_events); + free_time_events(g->time_events); + free_time_events(g->dead_time_events); + + if (g->cleanup_source) { + g_source_destroy(g->cleanup_source); + g_source_unref(g->cleanup_source); + } + + g_main_context_unref(g->glib_main_context); + pa_xfree(g); +} + +pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) { + assert(g); + return &g->api; +} + +static gboolean free_dead_events(gpointer p) { + pa_glib_mainloop *g = p; + assert(g); + + free_io_events(g->dead_io_events); + free_defer_events(g->dead_defer_events); + free_time_events(g->dead_time_events); + + g->dead_io_events = NULL; + g->dead_defer_events = NULL; + g->dead_time_events = NULL; + + g_source_destroy(g->cleanup_source); + g_source_unref(g->cleanup_source); + g->cleanup_source = NULL; + + return FALSE; +} + +static void schedule_free_dead_events(pa_glib_mainloop *g) { + assert(g && g->glib_main_context); + + if (g->cleanup_source) + return; + + g->cleanup_source = g_idle_source_new(); + assert(g->cleanup_source); + g_source_set_callback(g->cleanup_source, free_dead_events, g, NULL); + g_source_attach(g->cleanup_source, g->glib_main_context); +} diff --git a/src/polyp/glib-mainloop.h b/src/polyp/glib-mainloop.h new file mode 100644 index 00000000..b4815ed9 --- /dev/null +++ b/src/polyp/glib-mainloop.h @@ -0,0 +1,55 @@ +#ifndef fooglibmainloophfoo +#define fooglibmainloophfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include + +/** \file + * GLIB main loop support */ + +PA_C_DECL_BEGIN + +/** \pa_glib_mainloop + * An opaque GLIB main loop object */ +typedef struct pa_glib_mainloop pa_glib_mainloop; + +/** Create a new GLIB main loop object for the specified GLIB main loop context. If c is NULL the default context is used. */ +#if GLIB_MAJOR_VERSION >= 2 +pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c); +#else +pa_glib_mainloop *pa_glib_mainloop_new(void); +#endif + + +/** Free the GLIB main loop object */ +void pa_glib_mainloop_free(pa_glib_mainloop* g); + +/** Return the abstract main loop API vtable for the GLIB main loop object */ +pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/glib12-mainloop.c b/src/polyp/glib12-mainloop.c new file mode 100644 index 00000000..80a02b1c --- /dev/null +++ b/src/polyp/glib12-mainloop.c @@ -0,0 +1,500 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include + +/* A mainloop implementation based on GLIB 1.2 */ + +struct pa_io_event { + pa_glib_mainloop *mainloop; + int dead; + GIOChannel *io_channel; + guint source; + GIOCondition io_condition; + int fd; + void (*callback) (pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api *m, pa_io_event*e, void *userdata); + pa_io_event *next, *prev; +}; + +struct pa_time_event { + pa_glib_mainloop *mainloop; + int dead; + guint source; + struct timeval timeval; + void (*callback) (pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api *m, pa_time_event*e, void *userdata); + pa_time_event *next, *prev; +}; + +struct pa_defer_event { + pa_glib_mainloop *mainloop; + int dead; + guint source; + void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api *m, pa_defer_event*e, void *userdata); + pa_defer_event *next, *prev; +}; + +struct pa_glib_mainloop { + pa_mainloop_api api; + guint cleanup_source; + pa_io_event *io_events, *dead_io_events; + pa_time_event *time_events, *dead_time_events; + pa_defer_event *defer_events, *dead_defer_events; +}; + +static void schedule_free_dead_events(pa_glib_mainloop *g); + +static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f); + +static pa_io_event* glib_io_new(pa_mainloop_api*m, int fd, pa_io_event_flags_t f, void (*callback) (pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags_t f, void *userdata), void *userdata) { + pa_io_event *e; + pa_glib_mainloop *g; + + assert(m && m->userdata && fd >= 0 && callback); + g = m->userdata; + + e = pa_xmalloc(sizeof(pa_io_event)); + e->mainloop = m->userdata; + e->dead = 0; + e->fd = fd; + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + + e->io_channel = g_io_channel_unix_new(e->fd); + assert(e->io_channel); + e->source = (guint) -1; + e->io_condition = 0; + + glib_io_enable(e, f); + + e->next = g->io_events; + if (e->next) e->next->prev = e; + g->io_events = e; + e->prev = NULL; + + return e; +} + +static gboolean io_cb(GIOChannel *source, GIOCondition condition, gpointer data) { + pa_io_event *e = data; + pa_io_event_flags_t f; + assert(source && e && e->io_channel == source); + + f = (condition & G_IO_IN ? PA_IO_EVENT_INPUT : 0) | + (condition & G_IO_OUT ? PA_IO_EVENT_OUTPUT : 0) | + (condition & G_IO_ERR ? PA_IO_EVENT_ERROR : 0) | + (condition & G_IO_HUP ? PA_IO_EVENT_HANGUP : 0); + + e->callback(&e->mainloop->api, e, e->fd, f, e->userdata); + return TRUE; +} + +static void glib_io_enable(pa_io_event*e, pa_io_event_flags_t f) { + GIOCondition c; + assert(e && !e->dead); + + c = (f & PA_IO_EVENT_INPUT ? G_IO_IN : 0) | (f & PA_IO_EVENT_OUTPUT ? G_IO_OUT : 0); + + if (c == e->io_condition) + return; + + if (e->source != (guint) -1) + g_source_remove(e->source); + + e->source = g_io_add_watch_full(e->io_channel, G_PRIORITY_DEFAULT, c | G_IO_ERR | G_IO_HUP, io_cb, e, NULL); + assert(e->source != (guint) -1); + e->io_condition = c; +} + +static void glib_io_free(pa_io_event*e) { + assert(e && !e->dead); + + if (e->source != (guint) -1) { + g_source_remove(e->source); + e->source = (guint) -1; + } + + if (e->prev) + e->prev->next = e->next; + else + e->mainloop->io_events = e->next; + + if (e->next) + e->next->prev = e->prev; + + if ((e->next = e->mainloop->dead_io_events)) + e->next->prev = e; + + e->mainloop->dead_io_events = e; + e->prev = NULL; + + e->dead = 1; + schedule_free_dead_events(e->mainloop); +} + +static void glib_io_set_destroy(pa_io_event*e, void (*callback)(pa_mainloop_api*m, pa_io_event *e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* Time sources */ + +static void glib_time_restart(pa_time_event*e, const struct timeval *tv); + +static pa_time_event* glib_time_new(pa_mainloop_api*m, const struct timeval *tv, void (*callback) (pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) { + pa_glib_mainloop *g; + pa_time_event *e; + + assert(m && m->userdata && tv && callback); + g = m->userdata; + + e = pa_xmalloc(sizeof(pa_time_event)); + e->mainloop = g; + e->dead = 0; + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + e->source = (guint) -1; + + glib_time_restart(e, tv); + + e->next = g->time_events; + if (e->next) e->next->prev = e; + g->time_events = e; + e->prev = NULL; + + return e; +} + +static guint msec_diff(const struct timeval *a, const struct timeval *b) { + guint r; + assert(a && b); + + if (a->tv_sec < b->tv_sec) + return 0; + + if (a->tv_sec == b->tv_sec && a->tv_sec <= b->tv_sec) + return 0; + + r = (a->tv_sec-b->tv_sec)*1000; + + if (a->tv_usec >= b->tv_usec) + r += (a->tv_usec - b->tv_usec) / 1000; + else + r -= (b->tv_usec - a->tv_usec) / 1000; + + return r; +} + +static gboolean time_cb(gpointer data) { + pa_time_event* e = data; + assert(e && e->mainloop && e->source != (guint) -1); + + g_source_remove(e->source); + e->source = (guint) -1; + + e->callback(&e->mainloop->api, e, &e->timeval, e->userdata); + return FALSE; +} + +static void glib_time_restart(pa_time_event*e, const struct timeval *tv) { + struct timeval now; + assert(e && e->mainloop && !e->dead); + + pa_gettimeofday(&now); + if (e->source != (guint) -1) + g_source_remove(e->source); + + if (tv) { + e->timeval = *tv; + e->source = g_timeout_add_full(G_PRIORITY_DEFAULT, msec_diff(tv, &now), time_cb, e, NULL); + assert(e->source != (guint) -1); + } else + e->source = (guint) -1; + } + +static void glib_time_free(pa_time_event *e) { + assert(e && e->mainloop && !e->dead); + + if (e->source != (guint) -1) { + g_source_remove(e->source); + e->source = (guint) -1; + } + + if (e->prev) + e->prev->next = e->next; + else + e->mainloop->time_events = e->next; + + if (e->next) + e->next->prev = e->prev; + + if ((e->next = e->mainloop->dead_time_events)) + e->next->prev = e; + + e->mainloop->dead_time_events = e; + e->prev = NULL; + + e->dead = 1; + schedule_free_dead_events(e->mainloop); +} + +static void glib_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*m, pa_time_event*e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* Deferred sources */ + +static void glib_defer_enable(pa_defer_event *e, int b); + +static pa_defer_event* glib_defer_new(pa_mainloop_api*m, void (*callback) (pa_mainloop_api*m, pa_defer_event *e, void *userdata), void *userdata) { + pa_defer_event *e; + pa_glib_mainloop *g; + + assert(m && m->userdata && callback); + g = m->userdata; + + e = pa_xmalloc(sizeof(pa_defer_event)); + e->mainloop = g; + e->dead = 0; + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + e->source = (guint) -1; + + glib_defer_enable(e, 1); + + e->next = g->defer_events; + if (e->next) e->next->prev = e; + g->defer_events = e; + e->prev = NULL; + return e; +} + +static gboolean idle_cb(gpointer data) { + pa_defer_event* e = data; + assert(e && e->mainloop && e->source != (guint) -1); + + e->callback(&e->mainloop->api, e, e->userdata); + return TRUE; +} + +static void glib_defer_enable(pa_defer_event *e, int b) { + assert(e && e->mainloop); + + if (e->source != (guint) -1 && !b) { + g_source_remove(e->source); + e->source = (guint) -1; + } else if (e->source == (guint) -1 && b) { + e->source = g_idle_add_full(G_PRIORITY_HIGH, idle_cb, e, NULL); + assert(e->source != (guint) -1); + } +} + +static void glib_defer_free(pa_defer_event *e) { + assert(e && e->mainloop && !e->dead); + + if (e->source != (guint) -1) { + g_source_remove(e->source); + e->source = (guint) -1; + } + + if (e->prev) + e->prev->next = e->next; + else + e->mainloop->defer_events = e->next; + + if (e->next) + e->next->prev = e->prev; + + if ((e->next = e->mainloop->dead_defer_events)) + e->next->prev = e; + + e->mainloop->dead_defer_events = e; + e->prev = NULL; + + e->dead = 1; + schedule_free_dead_events(e->mainloop); +} + +static void glib_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api *m, pa_defer_event *e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* quit() */ + +static void glib_quit(pa_mainloop_api*a, PA_GCC_UNUSED int retval) { + pa_glib_mainloop *g; + assert(a && a->userdata); + g = a->userdata; + + /* NOOP */ +} + +static const pa_mainloop_api vtable = { + .userdata = NULL, + + .io_new = glib_io_new, + .io_enable = glib_io_enable, + .io_free = glib_io_free, + .io_set_destroy= glib_io_set_destroy, + + .time_new = glib_time_new, + .time_restart = glib_time_restart, + .time_free = glib_time_free, + .time_set_destroy = glib_time_set_destroy, + + .defer_new = glib_defer_new, + .defer_enable = glib_defer_enable, + .defer_free = glib_defer_free, + .defer_set_destroy = glib_defer_set_destroy, + + .quit = glib_quit, +}; + +pa_glib_mainloop *pa_glib_mainloop_new(void) { + pa_glib_mainloop *g; + + g = pa_xmalloc(sizeof(pa_glib_mainloop)); + + g->api = vtable; + g->api.userdata = g; + + g->io_events = g->dead_io_events = NULL; + g->time_events = g->dead_time_events = NULL; + g->defer_events = g->dead_defer_events = NULL; + + g->cleanup_source = (guint) -1; + return g; +} + +static void free_io_events(pa_io_event *e) { + while (e) { + pa_io_event *r = e; + e = r->next; + + if (r->source != (guint) -1) + g_source_remove(r->source); + + if (r->io_channel) + g_io_channel_unref(r->io_channel); + + if (r->destroy_callback) + r->destroy_callback(&r->mainloop->api, r, r->userdata); + + pa_xfree(r); + } +} + +static void free_time_events(pa_time_event *e) { + while (e) { + pa_time_event *r = e; + e = r->next; + + if (r->source != (guint) -1) + g_source_remove(r->source); + + if (r->destroy_callback) + r->destroy_callback(&r->mainloop->api, r, r->userdata); + + pa_xfree(r); + } +} + +static void free_defer_events(pa_defer_event *e) { + while (e) { + pa_defer_event *r = e; + e = r->next; + + if (r->source != (guint) -1) + g_source_remove(r->source); + + if (r->destroy_callback) + r->destroy_callback(&r->mainloop->api, r, r->userdata); + + pa_xfree(r); + } +} + +void pa_glib_mainloop_free(pa_glib_mainloop* g) { + assert(g); + + free_io_events(g->io_events); + free_io_events(g->dead_io_events); + free_defer_events(g->defer_events); + free_defer_events(g->dead_defer_events); + free_time_events(g->time_events); + free_time_events(g->dead_time_events); + + if (g->cleanup_source != (guint) -1) + g_source_remove(g->cleanup_source); + + pa_xfree(g); +} + +pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g) { + assert(g); + return &g->api; +} + +static gboolean free_dead_events(gpointer p) { + pa_glib_mainloop *g = p; + assert(g); + + free_io_events(g->dead_io_events); + free_defer_events(g->dead_defer_events); + free_time_events(g->dead_time_events); + + g->dead_io_events = NULL; + g->dead_defer_events = NULL; + g->dead_time_events = NULL; + + g_source_remove(g->cleanup_source); + g->cleanup_source = (guint) -1; + + return FALSE; +} + +static void schedule_free_dead_events(pa_glib_mainloop *g) { + assert(g); + + if (g->cleanup_source != (guint) -1) + return; + + g->cleanup_source = g_idle_add_full(G_PRIORITY_HIGH, free_dead_events, g, NULL); +} diff --git a/src/polyp/mainloop-api.c b/src/polyp/mainloop-api.c new file mode 100644 index 00000000..a3eaee9c --- /dev/null +++ b/src/polyp/mainloop-api.c @@ -0,0 +1,68 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "mainloop-api.h" +#include +#include + +struct once_info { + void (*callback)(pa_mainloop_api*m, void *userdata); + void *userdata; +}; + +static void once_callback(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { + struct once_info *i = userdata; + assert(m && i && i->callback); + + i->callback(m, i->userdata); + + assert(m->defer_free); + m->defer_free(e); +} + +static void free_callback(pa_mainloop_api *m, PA_GCC_UNUSED pa_defer_event *e, void *userdata) { + struct once_info *i = userdata; + assert(m && i); + pa_xfree(i); +} + +void pa_mainloop_api_once(pa_mainloop_api* m, void (*callback)(pa_mainloop_api *m, void *userdata), void *userdata) { + struct once_info *i; + pa_defer_event *e; + assert(m && callback); + + i = pa_xnew(struct once_info, 1); + i->callback = callback; + i->userdata = userdata; + + assert(m->defer_new); + e = m->defer_new(m, once_callback, i); + assert(e); + m->defer_set_destroy(e, free_callback); +} + diff --git a/src/polyp/mainloop-api.h b/src/polyp/mainloop-api.h new file mode 100644 index 00000000..91ee4111 --- /dev/null +++ b/src/polyp/mainloop-api.h @@ -0,0 +1,120 @@ +#ifndef foomainloopapihfoo +#define foomainloopapihfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +#include + +/** \file + * + * Main loop abstraction layer. Both the polypaudio core and the + * polypaudio client library use a main loop abstraction layer. Due to + * this it is possible to embed polypaudio into other + * applications easily. Two main loop implemenations are + * currently available: + * \li A minimal implementation based on the C library's poll() function (See \ref mainloop.h) + * \li A wrapper around the GLIB main loop. Use this to embed polypaudio into your GLIB/GTK+/GNOME programs (See \ref glib-mainloop.h) + * + * The structure pa_mainloop_api is used as vtable for the main loop abstraction. + * + * This mainloop abstraction layer has no direct support for UNIX signals. Generic, mainloop implementation agnostic support is available throught \ref mainloop-signal.h. + * */ + +PA_C_DECL_BEGIN + +/** A bitmask for IO events */ +typedef enum pa_io_event_flags { + PA_IO_EVENT_NULL = 0, /**< No event */ + PA_IO_EVENT_INPUT = 1, /**< Input event */ + PA_IO_EVENT_OUTPUT = 2, /**< Output event */ + PA_IO_EVENT_HANGUP = 4, /**< Hangup event */ + PA_IO_EVENT_ERROR = 8 /**< Error event */ +} pa_io_event_flags_t; + +/** \pa_io_event + * An opaque IO event source object */ +typedef struct pa_io_event pa_io_event; + +/** \pa_defer_event + * An opaque deferred event source object. Events of this type are triggered once in every main loop iteration */ +typedef struct pa_defer_event pa_defer_event; + +/** \pa_time_event + * An opaque timer event source object */ +typedef struct pa_time_event pa_time_event; + +/** An abstract mainloop API vtable */ +typedef struct pa_mainloop_api pa_mainloop_api; + +struct pa_mainloop_api { + /** A pointer to some private, arbitrary data of the main loop implementation */ + void *userdata; + + /** Create a new IO event source object */ + pa_io_event* (*io_new)(pa_mainloop_api*a, int fd, pa_io_event_flags_t events, void (*callback) (pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata), void *userdata); + + /** Enable or disable IO events on this object */ + void (*io_enable)(pa_io_event* e, pa_io_event_flags_t events); + + /** Free a IO event source object */ + void (*io_free)(pa_io_event* e); + + /** Set a function that is called when the IO event source is destroyed. Use this to free the userdata argument if required */ + void (*io_set_destroy)(pa_io_event *e, void (*callback) (pa_mainloop_api*a, pa_io_event *e, void *userdata)); + + /** Create a new timer event source object for the specified Unix time */ + pa_time_event* (*time_new)(pa_mainloop_api*a, const struct timeval *tv, void (*callback) (pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata), void *userdata); + + /** Restart a running or expired timer event source with a new Unix time */ + void (*time_restart)(pa_time_event* e, const struct timeval *tv); + + /** Free a deferred timer event source object */ + void (*time_free)(pa_time_event* e); + + /** Set a function that is called when the timer event source is destroyed. Use this to free the userdata argument if required */ + void (*time_set_destroy)(pa_time_event *e, void (*callback) (pa_mainloop_api*a, pa_time_event *e, void *userdata)); + + /** Create a new deferred event source object */ + pa_defer_event* (*defer_new)(pa_mainloop_api*a, void (*callback) (pa_mainloop_api*a, pa_defer_event* e, void *userdata), void *userdata); + + /** Enable or disable a deferred event source temporarily */ + void (*defer_enable)(pa_defer_event* e, int b); + + /** Free a deferred event source object */ + void (*defer_free)(pa_defer_event* e); + + /** Set a function that is called when the deferred event source is destroyed. Use this to free the userdata argument if required */ + void (*defer_set_destroy)(pa_defer_event *e, void (*callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata)); + + /** Exit the main loop and return the specfied retval*/ + void (*quit)(pa_mainloop_api*a, int retval); +}; + +/** Run the specified callback function once from the main loop using an anonymous defer event. */ +void pa_mainloop_api_once(pa_mainloop_api*m, void (*callback)(pa_mainloop_api*m, void *userdata), void *userdata); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/mainloop-signal.c b/src/polyp/mainloop-signal.c new file mode 100644 index 00000000..a03c9159 --- /dev/null +++ b/src/polyp/mainloop-signal.c @@ -0,0 +1,267 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_WINDOWS_H +#include +#endif + +#include +#include +#include +#include +#include + +struct pa_signal_event { + int sig; +#ifdef HAVE_SIGACTION + struct sigaction saved_sigaction; +#else + void (*saved_handler)(int sig); +#endif + void (*callback) (pa_mainloop_api*a, pa_signal_event *e, int sig, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api*a, pa_signal_event*e, void *userdata); + pa_signal_event *previous, *next; +}; + +static pa_mainloop_api *api = NULL; +static int signal_pipe[2] = { -1, -1 }; +static pa_io_event* io_event = NULL; +static pa_defer_event *defer_event = NULL; +static pa_signal_event *signals = NULL; + +#ifdef OS_IS_WIN32 +static unsigned int waiting_signals = 0; +static CRITICAL_SECTION crit; +#endif + +static void signal_handler(int sig) { +#ifndef HAVE_SIGACTION + signal(sig, signal_handler); +#endif + write(signal_pipe[1], &sig, sizeof(sig)); + +#ifdef OS_IS_WIN32 + EnterCriticalSection(&crit); + waiting_signals++; + LeaveCriticalSection(&crit); +#endif +} + +static void dispatch(pa_mainloop_api*a, int sig) { + pa_signal_event*s; + + for (s = signals; s; s = s->next) + if (s->sig == sig) { + assert(s->callback); + s->callback(a, s, sig, s->userdata); + break; + } +} + +static void defer(pa_mainloop_api*a, PA_GCC_UNUSED pa_defer_event*e, PA_GCC_UNUSED void *userdata) { + ssize_t r; + int sig; + unsigned int sigs; + +#ifdef OS_IS_WIN32 + EnterCriticalSection(&crit); + sigs = waiting_signals; + waiting_signals = 0; + LeaveCriticalSection(&crit); +#endif + + while (sigs) { + if ((r = read(signal_pipe[0], &sig, sizeof(sig))) < 0) { + pa_log(__FILE__": read(): %s\n", strerror(errno)); + return; + } + + if (r != sizeof(sig)) { + pa_log(__FILE__": short read()\n"); + return; + } + + dispatch(a, sig); + + sigs--; + } +} + +static void callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags_t f, PA_GCC_UNUSED void *userdata) { + ssize_t r; + int sig; + assert(a && e && f == PA_IO_EVENT_INPUT && e == io_event && fd == signal_pipe[0]); + + + if ((r = read(signal_pipe[0], &sig, sizeof(sig))) < 0) { + if (errno == EAGAIN) + return; + + pa_log(__FILE__": read(): %s\n", strerror(errno)); + return; + } + + if (r != sizeof(sig)) { + pa_log(__FILE__": short read()\n"); + return; + } + + dispatch(a, sig); +} + +int pa_signal_init(pa_mainloop_api *a) { + assert(!api && a && signal_pipe[0] == -1 && signal_pipe[1] == -1 && !io_event && !defer_event); + +#ifdef OS_IS_WIN32 + if (_pipe(signal_pipe, 200, _O_BINARY) < 0) { +#else + if (pipe(signal_pipe) < 0) { +#endif + pa_log(__FILE__": pipe() failed: %s\n", strerror(errno)); + return -1; + } + + pa_make_nonblock_fd(signal_pipe[0]); + pa_make_nonblock_fd(signal_pipe[1]); + pa_fd_set_cloexec(signal_pipe[0], 1); + pa_fd_set_cloexec(signal_pipe[1], 1); + + api = a; + +#ifndef OS_IS_WIN32 + io_event = api->io_new(api, signal_pipe[0], PA_IO_EVENT_INPUT, callback, NULL); + assert(io_event); +#else + defer_event = api->defer_new(api, defer, NULL); + assert(defer_event); + + InitializeCriticalSection(&crit); +#endif + + return 0; +} + +void pa_signal_done(void) { + assert(api && signal_pipe[0] >= 0 && signal_pipe[1] >= 0 && (io_event || defer_event)); + + while (signals) + pa_signal_free(signals); + + +#ifndef OS_IS_WIN32 + api->io_free(io_event); + io_event = NULL; +#else + api->defer_free(defer_event); + defer_event = NULL; + + DeleteCriticalSection(&crit); +#endif + + close(signal_pipe[0]); + close(signal_pipe[1]); + signal_pipe[0] = signal_pipe[1] = -1; + + api = NULL; +} + +pa_signal_event* pa_signal_new(int sig, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata) { + pa_signal_event *e = NULL; + +#ifdef HAVE_SIGACTION + struct sigaction sa; +#endif + + assert(sig > 0 && _callback); + + for (e = signals; e; e = e->next) + if (e->sig == sig) + goto fail; + + e = pa_xmalloc(sizeof(pa_signal_event)); + e->sig = sig; + e->callback = _callback; + e->userdata = userdata; + e->destroy_callback = NULL; + +#ifdef HAVE_SIGACTION + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = signal_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if (sigaction(sig, &sa, &e->saved_sigaction) < 0) +#else + if ((e->saved_handler = signal(sig, signal_handler)) == SIG_ERR) +#endif + goto fail; + + e->previous = NULL; + e->next = signals; + signals = e; + + return e; +fail: + if (e) + pa_xfree(e); + return NULL; +} + +void pa_signal_free(pa_signal_event *e) { + assert(e); + + if (e->next) + e->next->previous = e->previous; + if (e->previous) + e->previous->next = e->next; + else + signals = e->next; + +#ifdef HAVE_SIGACTION + sigaction(e->sig, &e->saved_sigaction, NULL); +#else + signal(e->sig, e->saved_handler); +#endif + + if (e->destroy_callback) + e->destroy_callback(api, e, e->userdata); + + pa_xfree(e); +} + +void pa_signal_set_destroy(pa_signal_event *e, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata)) { + assert(e); + e->destroy_callback = _callback; +} diff --git a/src/polyp/mainloop-signal.h b/src/polyp/mainloop-signal.h new file mode 100644 index 00000000..76065b22 --- /dev/null +++ b/src/polyp/mainloop-signal.h @@ -0,0 +1,60 @@ +#ifndef foomainloopsignalhfoo +#define foomainloopsignalhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +PA_C_DECL_BEGIN + +/** \file + * UNIX signal support for main loops. In contrast to other + * main loop event sources such as timer and IO events, UNIX signal + * support requires modification of the global process + * environment. Due to this the generic main loop abstraction layer as + * defined in \ref mainloop-api.h doesn't have direct support for UNIX + * signals. However, you may hook signal support into an abstract main loop via the routines defined herein. + */ + +/** Initialize the UNIX signal subsystem and bind it to the specified main loop */ +int pa_signal_init(pa_mainloop_api *api); + +/** Cleanup the signal subsystem */ +void pa_signal_done(void); + +/** \pa_signal_event + * An opaque UNIX signal event source object */ +typedef struct pa_signal_event pa_signal_event; + +/** Create a new UNIX signal event source object */ +pa_signal_event* pa_signal_new(int sig, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata); + +/** Free a UNIX signal event source object */ +void pa_signal_free(pa_signal_event *e); + +/** Set a function that is called when the signal event source is destroyed. Use this to free the userdata argument if required */ +void pa_signal_set_destroy(pa_signal_event *e, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata)); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/mainloop.c b/src/polyp/mainloop.c new file mode 100644 index 00000000..3fa9245c --- /dev/null +++ b/src/polyp/mainloop.c @@ -0,0 +1,812 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_POLL_H +#include +#else +#include "poll.h" +#endif + +#include + +#include "mainloop.h" +#include +#include +#include +#include + +struct pa_io_event { + pa_mainloop *mainloop; + int dead; + int fd; + pa_io_event_flags_t events; + void (*callback) (pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata); + struct pollfd *pollfd; + void *userdata; + void (*destroy_callback) (pa_mainloop_api*a, pa_io_event *e, void *userdata); +}; + +struct pa_time_event { + pa_mainloop *mainloop; + int dead; + int enabled; + struct timeval timeval; + void (*callback)(pa_mainloop_api*a, pa_time_event *e, const struct timeval*tv, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api*a, pa_time_event *e, void *userdata); +}; + +struct pa_defer_event { + pa_mainloop *mainloop; + int dead; + int enabled; + void (*callback)(pa_mainloop_api*a, pa_defer_event*e, void *userdata); + void *userdata; + void (*destroy_callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata); +}; + +struct pa_mainloop { + pa_idxset *io_events, *time_events, *defer_events; + int io_events_scan_dead, defer_events_scan_dead, time_events_scan_dead; + + struct pollfd *pollfds; + unsigned max_pollfds, n_pollfds; + int rebuild_pollfds; + + int prepared_timeout; + + int quit, retval; + pa_mainloop_api api; + + int deferred_pending; + + int wakeup_pipe[2]; + + enum { + STATE_PASSIVE, + STATE_PREPARED, + STATE_POLLING, + STATE_POLLED, + STATE_QUIT + } state; +}; + +/* IO events */ +static pa_io_event* mainloop_io_new( + pa_mainloop_api*a, + int fd, + pa_io_event_flags_t events, + void (*callback) (pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata), + void *userdata) { + + pa_mainloop *m; + pa_io_event *e; + + assert(a && a->userdata && fd >= 0 && callback); + m = a->userdata; + assert(a == &m->api); + + pa_mainloop_wakeup(m); + + e = pa_xmalloc(sizeof(pa_io_event)); + e->mainloop = m; + e->dead = 0; + + e->fd = fd; + e->events = events; + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + e->pollfd = NULL; + +#ifdef OS_IS_WIN32 + { + fd_set xset; + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = 0; + + FD_ZERO (&xset); + FD_SET (fd, &xset); + + if ((select((SELECT_TYPE_ARG1) fd, NULL, NULL, SELECT_TYPE_ARG234 &xset, + SELECT_TYPE_ARG5 &tv) == -1) && + (WSAGetLastError() == WSAENOTSOCK)) { + pa_log_warn(__FILE__": WARNING: cannot monitor non-socket file descriptors.\n"); + e->dead = 1; + } + } +#endif + + pa_idxset_put(m->io_events, e, NULL); + m->rebuild_pollfds = 1; + return e; +} + +static void mainloop_io_enable(pa_io_event *e, pa_io_event_flags_t events) { + assert(e && e->mainloop); + + pa_mainloop_wakeup(e->mainloop); + + e->events = events; + e->mainloop->rebuild_pollfds = 1; +} + +static void mainloop_io_free(pa_io_event *e) { + assert(e && e->mainloop); + + pa_mainloop_wakeup(e->mainloop); + + e->dead = e->mainloop->io_events_scan_dead = e->mainloop->rebuild_pollfds = 1; +} + +static void mainloop_io_set_destroy(pa_io_event *e, void (*callback)(pa_mainloop_api*a, pa_io_event *e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* Defer events */ +static pa_defer_event* mainloop_defer_new(pa_mainloop_api*a, void (*callback) (pa_mainloop_api*a, pa_defer_event *e, void *userdata), void *userdata) { + pa_mainloop *m; + pa_defer_event *e; + + assert(a && a->userdata && callback); + m = a->userdata; + assert(a == &m->api); + + e = pa_xmalloc(sizeof(pa_defer_event)); + e->mainloop = m; + e->dead = 0; + + e->enabled = 1; + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + + pa_idxset_put(m->defer_events, e, NULL); + + m->deferred_pending++; + return e; +} + +static void mainloop_defer_enable(pa_defer_event *e, int b) { + assert(e); + + if (e->enabled && !b) { + assert(e->mainloop->deferred_pending > 0); + e->mainloop->deferred_pending--; + } else if (!e->enabled && b) + e->mainloop->deferred_pending++; + + e->enabled = b; +} + +static void mainloop_defer_free(pa_defer_event *e) { + assert(e); + e->dead = e->mainloop->defer_events_scan_dead = 1; + + if (e->enabled) { + e->enabled = 0; + assert(e->mainloop->deferred_pending > 0); + e->mainloop->deferred_pending--; + } +} + +static void mainloop_defer_set_destroy(pa_defer_event *e, void (*callback)(pa_mainloop_api*a, pa_defer_event *e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* Time events */ +static pa_time_event* mainloop_time_new(pa_mainloop_api*a, const struct timeval *tv, void (*callback) (pa_mainloop_api*a, pa_time_event*e, const struct timeval *tv, void *userdata), void *userdata) { + pa_mainloop *m; + pa_time_event *e; + + assert(a && a->userdata && callback); + m = a->userdata; + assert(a == &m->api); + + pa_mainloop_wakeup(m); + + e = pa_xmalloc(sizeof(pa_time_event)); + e->mainloop = m; + e->dead = 0; + + e->enabled = !!tv; + if (tv) + e->timeval = *tv; + + e->callback = callback; + e->userdata = userdata; + e->destroy_callback = NULL; + + pa_idxset_put(m->time_events, e, NULL); + + return e; +} + +static void mainloop_time_restart(pa_time_event *e, const struct timeval *tv) { + assert(e); + + pa_mainloop_wakeup(e->mainloop); + + if (tv) { + e->enabled = 1; + e->timeval = *tv; + } else + e->enabled = 0; +} + +static void mainloop_time_free(pa_time_event *e) { + assert(e); + + pa_mainloop_wakeup(e->mainloop); + + e->dead = e->mainloop->time_events_scan_dead = 1; +} + +static void mainloop_time_set_destroy(pa_time_event *e, void (*callback)(pa_mainloop_api*a, pa_time_event *e, void *userdata)) { + assert(e); + e->destroy_callback = callback; +} + +/* quit() */ + +static void mainloop_quit(pa_mainloop_api*a, int retval) { + pa_mainloop *m; + assert(a && a->userdata); + m = a->userdata; + assert(a == &m->api); + + pa_mainloop_wakeup(m); + + m->quit = 1; + m->retval = retval; +} + +static const pa_mainloop_api vtable = { + .userdata = NULL, + + .io_new= mainloop_io_new, + .io_enable= mainloop_io_enable, + .io_free= mainloop_io_free, + .io_set_destroy= mainloop_io_set_destroy, + + .time_new = mainloop_time_new, + .time_restart = mainloop_time_restart, + .time_free = mainloop_time_free, + .time_set_destroy = mainloop_time_set_destroy, + + .defer_new = mainloop_defer_new, + .defer_enable = mainloop_defer_enable, + .defer_free = mainloop_defer_free, + .defer_set_destroy = mainloop_defer_set_destroy, + + .quit = mainloop_quit, +}; + +pa_mainloop *pa_mainloop_new(void) { + pa_mainloop *m; + + m = pa_xmalloc(sizeof(pa_mainloop)); + +#ifndef OS_ISWIN32 + if (pipe(m->wakeup_pipe) < 0) { + pa_xfree(m); + return NULL; + } + + pa_make_nonblock_fd(m->wakeup_pipe[0]); + pa_make_nonblock_fd(m->wakeup_pipe[1]); +#else + m->wakeup_pipe[0] = -1; + m->wakeup_pipe[1] = -1; +#endif + + m->io_events = pa_idxset_new(NULL, NULL); + m->defer_events = pa_idxset_new(NULL, NULL); + m->time_events = pa_idxset_new(NULL, NULL); + + assert(m->io_events && m->defer_events && m->time_events); + + m->io_events_scan_dead = m->defer_events_scan_dead = m->time_events_scan_dead = 0; + + m->pollfds = NULL; + m->max_pollfds = m->n_pollfds = m->rebuild_pollfds = 0; + + m->quit = m->retval = 0; + + m->api = vtable; + m->api.userdata = m; + + m->deferred_pending = 0; + + m->state = STATE_PASSIVE; + + return m; +} + +static int io_foreach(void *p, uint32_t PA_GCC_UNUSED idx, int *del, void*userdata) { + pa_io_event *e = p; + int *all = userdata; + assert(e && del && all); + + if (!*all && !e->dead) + return 0; + + if (e->destroy_callback) + e->destroy_callback(&e->mainloop->api, e, e->userdata); + pa_xfree(e); + *del = 1; + return 0; +} + +static int time_foreach(void *p, uint32_t PA_GCC_UNUSED idx, int *del, void*userdata) { + pa_time_event *e = p; + int *all = userdata; + assert(e && del && all); + + if (!*all && !e->dead) + return 0; + + if (e->destroy_callback) + e->destroy_callback(&e->mainloop->api, e, e->userdata); + pa_xfree(e); + *del = 1; + return 0; +} + +static int defer_foreach(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void*userdata) { + pa_defer_event *e = p; + int *all = userdata; + assert(e && del && all); + + if (!*all && !e->dead) + return 0; + + if (e->destroy_callback) + e->destroy_callback(&e->mainloop->api, e, e->userdata); + pa_xfree(e); + *del = 1; + return 0; +} + +void pa_mainloop_free(pa_mainloop* m) { + int all = 1; + assert(m && (m->state != STATE_POLLING)); + + pa_idxset_foreach(m->io_events, io_foreach, &all); + pa_idxset_foreach(m->time_events, time_foreach, &all); + pa_idxset_foreach(m->defer_events, defer_foreach, &all); + + pa_idxset_free(m->io_events, NULL, NULL); + pa_idxset_free(m->time_events, NULL, NULL); + pa_idxset_free(m->defer_events, NULL, NULL); + + pa_xfree(m->pollfds); + + if (m->wakeup_pipe[0] >= 0) + close(m->wakeup_pipe[0]); + if (m->wakeup_pipe[1] >= 0) + close(m->wakeup_pipe[1]); + + pa_xfree(m); +} + +static void scan_dead(pa_mainloop *m) { + int all = 0; + assert(m); + + if (m->io_events_scan_dead) + pa_idxset_foreach(m->io_events, io_foreach, &all); + if (m->time_events_scan_dead) + pa_idxset_foreach(m->time_events, time_foreach, &all); + if (m->defer_events_scan_dead) + pa_idxset_foreach(m->defer_events, defer_foreach, &all); + + m->io_events_scan_dead = m->time_events_scan_dead = m->defer_events_scan_dead = 0; +} + +static void rebuild_pollfds(pa_mainloop *m) { + pa_io_event*e; + struct pollfd *p; + uint32_t idx = PA_IDXSET_INVALID; + unsigned l; + + l = pa_idxset_size(m->io_events) + 1; + if (m->max_pollfds < l) { + m->pollfds = pa_xrealloc(m->pollfds, sizeof(struct pollfd)*l); + m->max_pollfds = l; + } + + m->n_pollfds = 0; + p = m->pollfds; + + if (m->wakeup_pipe[0] >= 0) { + m->pollfds[0].fd = m->wakeup_pipe[0]; + m->pollfds[0].events = POLLIN; + m->pollfds[0].revents = 0; + p++; + m->n_pollfds++; + } + + for (e = pa_idxset_first(m->io_events, &idx); e; e = pa_idxset_next(m->io_events, &idx)) { + if (e->dead) { + e->pollfd = NULL; + continue; + } + + e->pollfd = p; + p->fd = e->fd; + p->events = + ((e->events & PA_IO_EVENT_INPUT) ? POLLIN : 0) | + ((e->events & PA_IO_EVENT_OUTPUT) ? POLLOUT : 0) | + POLLHUP | + POLLERR; + p->revents = 0; + + p++; + m->n_pollfds++; + } + + m->rebuild_pollfds = 0; +} + +static int dispatch_pollfds(pa_mainloop *m) { + uint32_t idx = PA_IDXSET_INVALID; + pa_io_event *e; + int r = 0; + + for (e = pa_idxset_first(m->io_events, &idx); e && !m->quit; e = pa_idxset_next(m->io_events, &idx)) { + if (e->dead || !e->pollfd || !e->pollfd->revents) + continue; + + assert(e->pollfd->fd == e->fd && e->callback); + e->callback(&m->api, e, e->fd, + (e->pollfd->revents & POLLHUP ? PA_IO_EVENT_HANGUP : 0) | + (e->pollfd->revents & POLLIN ? PA_IO_EVENT_INPUT : 0) | + (e->pollfd->revents & POLLOUT ? PA_IO_EVENT_OUTPUT : 0) | + (e->pollfd->revents & POLLERR ? PA_IO_EVENT_ERROR : 0), + e->userdata); + e->pollfd->revents = 0; + r++; + } + + return r; +} + +static int dispatch_defer(pa_mainloop *m) { + uint32_t idx; + pa_defer_event *e; + int r = 0; + + if (!m->deferred_pending) + return 0; + + for (e = pa_idxset_first(m->defer_events, &idx); e && !m->quit; e = pa_idxset_next(m->defer_events, &idx)) { + if (e->dead || !e->enabled) + continue; + + assert(e->callback); + e->callback(&m->api, e, e->userdata); + r++; + } + + return r; +} + +static int calc_next_timeout(pa_mainloop *m) { + uint32_t idx; + pa_time_event *e; + struct timeval now; + int t = -1; + int got_time = 0; + + if (pa_idxset_isempty(m->time_events)) + return -1; + + for (e = pa_idxset_first(m->time_events, &idx); e; e = pa_idxset_next(m->time_events, &idx)) { + int tmp; + + if (e->dead || !e->enabled) + continue; + + /* Let's save a system call */ + if (!got_time) { + pa_gettimeofday(&now); + got_time = 1; + } + + if (e->timeval.tv_sec < now.tv_sec || (e->timeval.tv_sec == now.tv_sec && e->timeval.tv_usec <= now.tv_usec)) + return 0; + + tmp = (e->timeval.tv_sec - now.tv_sec)*1000; + + if (e->timeval.tv_usec > now.tv_usec) + tmp += (e->timeval.tv_usec - now.tv_usec)/1000; + else + tmp -= (now.tv_usec - e->timeval.tv_usec)/1000; + + if (tmp == 0) + return 0; + else if (t == -1 || tmp < t) + t = tmp; + } + + return t; +} + +static int dispatch_timeout(pa_mainloop *m) { + uint32_t idx; + pa_time_event *e; + struct timeval now; + int got_time = 0; + int r = 0; + assert(m); + + if (pa_idxset_isempty(m->time_events)) + return 0; + + for (e = pa_idxset_first(m->time_events, &idx); e && !m->quit; e = pa_idxset_next(m->time_events, &idx)) { + + if (e->dead || !e->enabled) + continue; + + /* Let's save a system call */ + if (!got_time) { + pa_gettimeofday(&now); + got_time = 1; + } + + if (e->timeval.tv_sec < now.tv_sec || (e->timeval.tv_sec == now.tv_sec && e->timeval.tv_usec <= now.tv_usec)) { + assert(e->callback); + + e->enabled = 0; + e->callback(&m->api, e, &e->timeval, e->userdata); + + r++; + } + } + + return r; +} + +void pa_mainloop_wakeup(pa_mainloop *m) { + char c = 'W'; + assert(m); + + if (m->wakeup_pipe[1] >= 0) + write(m->wakeup_pipe[1], &c, sizeof(c)); +} + +static void clear_wakeup(pa_mainloop *m) { + char c[10]; + + assert(m); + + if (m->wakeup_pipe[0] < 0) + return; + + while (read(m->wakeup_pipe[0], &c, sizeof(c)) == sizeof(c)); +} + +int pa_mainloop_prepare(pa_mainloop *m, int timeout) { + int dispatched = 0; + + assert(m && (m->state == STATE_PASSIVE)); + + clear_wakeup(m); + + scan_dead(m); + + if (m->quit) + goto quit; + + dispatched += dispatch_defer(m); + + if (m->quit) + goto quit; + + if (m->rebuild_pollfds) + rebuild_pollfds(m); + + m->prepared_timeout = calc_next_timeout(m); + if ((timeout >= 0) && (m->prepared_timeout > timeout)) + m->prepared_timeout = timeout; + + m->state = STATE_PREPARED; + + return dispatched; + +quit: + + m->state = STATE_QUIT; + + return -2; +} + +int pa_mainloop_poll(pa_mainloop *m) { + int r; + + assert(m && (m->state == STATE_PREPARED)); + + m->state = STATE_POLLING; + + r = poll(m->pollfds, m->n_pollfds, m->prepared_timeout); + + if ((r < 0) && (errno == EINTR)) + r = 0; + + if (r < 0) + m->state = STATE_PASSIVE; + else + m->state = STATE_POLLED; + + return r; +} + +int pa_mainloop_dispatch(pa_mainloop *m) { + int dispatched = 0; + + assert(m && (m->state == STATE_POLLED)); + + dispatched += dispatch_timeout(m); + + if (m->quit) + goto quit; + + dispatched += dispatch_pollfds(m); + + if (m->quit) + goto quit; + + m->state = STATE_PASSIVE; + + return dispatched; + +quit: + + m->state = STATE_QUIT; + + return -2; +} + +int pa_mainloop_get_retval(pa_mainloop *m) { + assert(m); + return m->retval; +} + +int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval) { + int r, dispatched = 0; + + assert(m); + + r = pa_mainloop_prepare(m, block ? -1 : 0); + if (r < 0) { + if ((r == -2) && retval) + *retval = pa_mainloop_get_retval(m); + return r; + } + + dispatched += r; + + r = pa_mainloop_poll(m); + if (r < 0) { + pa_log(__FILE__": poll(): %s\n", strerror(errno)); + return r; + } + + r = pa_mainloop_dispatch(m); + if (r < 0) { + if ((r == -2) && retval) + *retval = pa_mainloop_get_retval(m); + return r; + } + + dispatched += r; + + return dispatched; +} + +int pa_mainloop_run(pa_mainloop *m, int *retval) { + int r; + while ((r = pa_mainloop_iterate(m, 1, retval)) >= 0); + + if (r == -2) + return 1; + else if (r < 0) + return -1; + else + return 0; +} + +void pa_mainloop_quit(pa_mainloop *m, int r) { + assert(m); + pa_mainloop_wakeup(m); + m->quit = r; +} + +pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m) { + assert(m); + return &m->api; +} + +int pa_mainloop_deferred_pending(pa_mainloop *m) { + assert(m); + return m->deferred_pending > 0; +} + + +#if 0 +void pa_mainloop_dump(pa_mainloop *m) { + assert(m); + + pa_log(__FILE__": Dumping mainloop sources START\n"); + + { + uint32_t idx = PA_IDXSET_INVALID; + pa_io_event *e; + for (e = pa_idxset_first(m->io_events, &idx); e; e = pa_idxset_next(m->io_events, &idx)) { + if (e->dead) + continue; + + pa_log(__FILE__": kind=io fd=%i events=%i callback=%p userdata=%p\n", e->fd, (int) e->events, (void*) e->callback, (void*) e->userdata); + } + } + { + uint32_t idx = PA_IDXSET_INVALID; + pa_defer_event *e; + for (e = pa_idxset_first(m->defer_events, &idx); e; e = pa_idxset_next(m->defer_events, &idx)) { + if (e->dead) + continue; + + pa_log(__FILE__": kind=defer enabled=%i callback=%p userdata=%p\n", e->enabled, (void*) e->callback, (void*) e->userdata); + } + } + { + uint32_t idx = PA_IDXSET_INVALID; + pa_time_event *e; + for (e = pa_idxset_first(m->time_events, &idx); e; e = pa_idxset_next(m->time_events, &idx)) { + if (e->dead) + continue; + + pa_log(__FILE__": kind=time enabled=%i time=%lu.%lu callback=%p userdata=%p\n", e->enabled, (unsigned long) e->timeval.tv_sec, (unsigned long) e->timeval.tv_usec, (void*) e->callback, (void*) e->userdata); + } + } + + pa_log(__FILE__": Dumping mainloop sources STOP\n"); + +} +#endif diff --git a/src/polyp/mainloop.h b/src/polyp/mainloop.h new file mode 100644 index 00000000..691f8c50 --- /dev/null +++ b/src/polyp/mainloop.h @@ -0,0 +1,90 @@ +#ifndef foomainloophfoo +#define foomainloophfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +PA_C_DECL_BEGIN + +/** \file + * + * A minimal main loop implementation based on the C library's poll() + * function. Using the routines defined herein you may create a simple + * main loop supporting the generic main loop abstraction layer as + * defined in \ref mainloop-api.h. This implementation is thread safe + * as long as you access the main loop object from a single thread only.*/ + +/** \pa_mainloop + * An opaque main loop object + */ +typedef struct pa_mainloop pa_mainloop; + +/** Allocate a new main loop object */ +pa_mainloop *pa_mainloop_new(void); + +/** Free a main loop object */ +void pa_mainloop_free(pa_mainloop* m); + + +/** Prepare for a single iteration of the main loop. Returns a negative value +on error or exit request. timeout specifies a maximum timeout for the subsequent +poll, or -1 for blocking behaviour. Defer events are also dispatched when this +function is called. On success returns the number of source dispatched in this +iteration.*/ +int pa_mainloop_prepare(pa_mainloop *m, int timeout); +/** Execute the previously prepared poll. Returns a negative value on error.*/ +int pa_mainloop_poll(pa_mainloop *m); +/** Dispatch timeout and io events from the previously executed poll. Returns +a negative value on error. On success returns the number of source dispatched. */ +int pa_mainloop_dispatch(pa_mainloop *m); + +/** Return the return value as specified with the main loop's quit() routine. */ +int pa_mainloop_get_retval(pa_mainloop *m); + +/** Run a single iteration of the main loop. This is a convenience function +for pa_mainloop_prepare(), pa_mainloop_poll() and pa_mainloop_dispatch(). +Returns a negative value on error or exit request. If block is nonzero, +block for events if none are queued. Optionally return the return value as +specified with the main loop's quit() routine in the integer variable retval points +to. On success returns the number of source dispatched in this iteration. */ +int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval); + +/** Run unlimited iterations of the main loop object until the main loop's quit() routine is called. */ +int pa_mainloop_run(pa_mainloop *m, int *retval); + +/** Return the abstract main loop abstraction layer vtable for this main loop. This calls pa_mainloop_iterate() iteratively.*/ +pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m); + +/** Return non-zero when there are any deferred events pending. \since 0.5 */ +int pa_mainloop_deferred_pending(pa_mainloop *m); + +/** Shutdown the main loop */ +void pa_mainloop_quit(pa_mainloop *m, int r); + +/** Interrupt a running poll (for threaded systems) */ +void pa_mainloop_wakeup(pa_mainloop *m); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/polyplib-browser.c b/src/polyp/polyplib-browser.c new file mode 100644 index 00000000..9a389484 --- /dev/null +++ b/src/polyp/polyplib-browser.c @@ -0,0 +1,312 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +#include "polyplib-browser.h" +#include +#include +#include + +#define SERVICE_NAME_SINK "_polypaudio-sink._tcp." +#define SERVICE_NAME_SOURCE "_polypaudio-source._tcp." +#define SERVICE_NAME_SERVER "_polypaudio-server._tcp." + +pa_browser { + int ref; + pa_mainloop_api *mainloop; + + void (*callback)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void *userdata); + void *callback_userdata; + + sw_discovery discovery; + pa_io_event *io_event; +}; + + +static void io_callback(pa_mainloop_api*a, pa_io_event*e, int fd, pa_io_event_flags events, void *userdata) { + pa_browser *b = userdata; + assert(a && b && b->mainloop == a); + + if (events != PA_IO_EVENT_INPUT || sw_discovery_read_socket(b->discovery) != SW_OKAY) { + pa_log(__FILE__": connection to HOWL daemon failed.\n"); + b->mainloop->io_free(b->io_event); + b->io_event = NULL; + return; + } +} + +static sw_result resolve_reply( + sw_discovery discovery, + sw_discovery_oid oid, + sw_uint32 interface_index, + sw_const_string name, + sw_const_string type, + sw_const_string domain, + sw_ipv4_address address, + sw_port port, + sw_octets text_record, + sw_ulong text_record_len, + sw_opaque extra) { + + pa_browser *b = extra; + pa_browse_info i; + char ip[256], a[256]; + pa_browse_opcode opcode; + int device_found = 0; + uint32_t cookie; + pa_typeid_t typeid; + pa_sample_spec ss; + int ss_valid = 0; + sw_text_record_iterator iterator; + int free_iterator = 0; + char *c = NULL; + + assert(b); + + sw_discovery_cancel(discovery, oid); + + memset(&i, 0, sizeof(i)); + i.name = name; + + if (!b->callback) + goto fail; + + if (!strcmp(type, SERVICE_NAME_SINK)) + opcode = PA_BROWSE_NEW_SINK; + else if (!strcmp(type, SERVICE_NAME_SOURCE)) + opcode = PA_BROWSE_NEW_SOURCE; + else if (!strcmp(type, SERVICE_NAME_SERVER)) + opcode = PA_BROWSE_NEW_SERVER; + else + goto fail; + + + snprintf(a, sizeof(a), "tcp:%s:%u", sw_ipv4_address_name(address, ip, sizeof(ip)), port); + i.server = a; + + if (text_record && text_record_len) { + char key[SW_TEXT_RECORD_MAX_LEN]; + uint8_t val[SW_TEXT_RECORD_MAX_LEN]; + uint32_t val_len; + + if (sw_text_record_iterator_init(&iterator, text_record, text_record_len) != SW_OKAY) { + pa_log("sw_text_record_string_iterator_init() failed.\n"); + goto fail; + } + + free_iterator = 1; + + while (sw_text_record_iterator_next(iterator, key, val, &val_len) == SW_OKAY) { + c = pa_xstrndup((char*) val, val_len); + + if (!strcmp(key, "device")) { + device_found = 1; + pa_xfree((char*) i.device); + i.device = c; + c = NULL; + } else if (!strcmp(key, "server-version")) { + pa_xfree((char*) i.server_version); + i.server_version = c; + c = NULL; + } else if (!strcmp(key, "user-name")) { + pa_xfree((char*) i.user_name); + i.user_name = c; + c = NULL; + } else if (!strcmp(key, "fqdn")) { + size_t l; + + pa_xfree((char*) i.fqdn); + i.fqdn = c; + c = NULL; + + l = strlen(a); + assert(l+1 <= sizeof(a)); + strncat(a, " ", sizeof(a)-l-1); + strncat(a, i.fqdn, sizeof(a)-l-2); + } else if (!strcmp(key, "cookie")) { + + if (pa_atou(c, &cookie) < 0) + goto fail; + + i.cookie = &cookie; + } else if (!strcmp(key, "description")) { + pa_xfree((char*) i.description); + i.description = c; + c = NULL; + } else if (!strcmp(key, "typeid")) { + + if (pa_atou(c, &typeid) < 0) + goto fail; + + i.typeid = &typeid; + } else if (!strcmp(key, "channels")) { + uint32_t ch; + + if (pa_atou(c, &ch) < 0 || ch <= 0 || ch > 255) + goto fail; + + ss.channels = (uint8_t) ch; + ss_valid |= 1; + + } else if (!strcmp(key, "rate")) { + if (pa_atou(c, &ss.rate) < 0) + goto fail; + ss_valid |= 2; + } else if (!strcmp(key, "format")) { + + if ((ss.format = pa_parse_sample_format(c)) == PA_SAMPLE_INVALID) + goto fail; + + ss_valid |= 4; + } + + pa_xfree(c); + c = NULL; + } + + } + + /* No device txt record was sent for a sink or source service */ + if (opcode != PA_BROWSE_NEW_SERVER && !device_found) + goto fail; + + if (ss_valid == 7) + i.sample_spec = &ss; + + + b->callback(b, opcode, &i, b->callback_userdata); + +fail: + pa_xfree((void*) i.device); + pa_xfree((void*) i.fqdn); + pa_xfree((void*) i.server_version); + pa_xfree((void*) i.user_name); + pa_xfree((void*) i.description); + pa_xfree(c); + + if (free_iterator) + sw_text_record_iterator_fina(iterator); + + + return SW_OKAY; +} + +static sw_result browse_reply( + sw_discovery discovery, + sw_discovery_oid id, + sw_discovery_browse_status status, + sw_uint32 interface_index, + sw_const_string name, + sw_const_string type, + sw_const_string domain, + sw_opaque extra) { + + pa_browser *b = extra; + assert(b); + + switch (status) { + case SW_DISCOVERY_BROWSE_ADD_SERVICE: { + sw_discovery_oid oid; + + if (sw_discovery_resolve(b->discovery, 0, name, type, domain, resolve_reply, b, &oid) != SW_OKAY) + pa_log("sw_discovery_resolve() failed\n"); + + break; + } + + case SW_DISCOVERY_BROWSE_REMOVE_SERVICE: + if (b->callback) { + pa_browse_info i; + memset(&i, 0, sizeof(i)); + i.name = name; + b->callback(b, PA_BROWSE_REMOVE, &i, b->callback_userdata); + } + break; + + default: + ; + } + + return SW_OKAY; +} + +pa_browser *pa_browser_new(pa_mainloop_api *mainloop) { + pa_browser *b; + sw_discovery_oid oid; + + b = pa_xmalloc(sizeof(pa_browser)); + b->mainloop = mainloop; + b->ref = 1; + b->callback = NULL; + b->callback_userdata = NULL; + + if (sw_discovery_init(&b->discovery) != SW_OKAY) { + pa_log("sw_discovery_init() failed.\n"); + pa_xfree(b); + return NULL; + } + + if (sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SERVER, NULL, browse_reply, b, &oid) != SW_OKAY || + sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SINK, NULL, browse_reply, b, &oid) != SW_OKAY || + sw_discovery_browse(b->discovery, 0, SERVICE_NAME_SOURCE, NULL, browse_reply, b, &oid) != SW_OKAY) { + + pa_log("sw_discovery_browse() failed.\n"); + + sw_discovery_fina(b->discovery); + pa_xfree(b); + return NULL; + } + + b->io_event = mainloop->io_new(mainloop, sw_discovery_socket(b->discovery), PA_IO_EVENT_INPUT, io_callback, b); + return b; +} + +static void browser_free(pa_browser *b) { + assert(b && b->mainloop); + + if (b->io_event) + b->mainloop->io_free(b->io_event); + + sw_discovery_fina(b->discovery); + pa_xfree(b); +} + +pa_browser *pa_browser_ref(pa_browser *b) { + assert(b && b->ref >= 1); + b->ref++; + return b; +} + +void pa_browser_unref(pa_browser *b) { + assert(b && b->ref >= 1); + + if ((-- (b->ref)) <= 0) + browser_free(b); +} + +void pa_browser_set_callback(pa_browser *b, void (*cb)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void* userdata), void *userdata) { + assert(b); + + b->callback = cb; + b->callback_userdata = userdata; +} diff --git a/src/polyp/polyplib-browser.h b/src/polyp/polyplib-browser.h new file mode 100644 index 00000000..853304d7 --- /dev/null +++ b/src/polyp/polyplib-browser.h @@ -0,0 +1,65 @@ +#ifndef foopolyplibbrowserhfoo +#define foopolyplibbrowserhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include +#include + +PA_C_DECL_BEGIN + +pa_browser; + +pa_browse_opcode { + PA_BROWSE_NEW_SERVER, + PA_BROWSE_NEW_SINK, + PA_BROWSE_NEW_SOURCE, + PA_BROWSE_REMOVE +}; + +pa_browser *pa_browser_new(pa_mainloop_api *mainloop); +pa_browser *pa_browser_ref(pa_browser *z); +void pa_browser_unref(pa_browser *z); + +pa_browse_info { + /* Unique service name */ + const char *name; /* always available */ + + /* Server info */ + const char *server; /* always available */ + const char *server_version, *user_name, *fqdn; /* optional */ + const uint32_t *cookie; /* optional */ + + /* Device info */ + const char *device; /* always available when this information is of a sink/source */ + const char *description; /* optional */ + const pa_typeid_t *typeid; /* optional */ + const pa_sample_spec *sample_spec; /* optional */ +}; + +void pa_browser_set_callback(pa_browser *z, void (*cb)(pa_browser *z, pa_browse_opcode c, const pa_browse_info *i, void *userdata), void *userdata); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/polyplib-context.c b/src/polyp/polyplib-context.c new file mode 100644 index 00000000..c392f0fc --- /dev/null +++ b/src/polyp/polyplib-context.c @@ -0,0 +1,871 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif + +#include + +#include "polyplib-internal.h" +#include "polyplib-context.h" +#include "polyplib-version.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_X11 +#include "client-conf-x11.h" +#endif + +#define AUTOSPAWN_LOCK "autospawn.lock" + +static const pa_pdispatch_callback command_table[PA_COMMAND_MAX] = { + [PA_COMMAND_REQUEST] = pa_command_request, + [PA_COMMAND_PLAYBACK_STREAM_KILLED] = pa_command_stream_killed, + [PA_COMMAND_RECORD_STREAM_KILLED] = pa_command_stream_killed, + [PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event +}; + +static void unlock_autospawn_lock_file(pa_context *c) { + assert(c); + + if (c->autospawn_lock_fd >= 0) { + char lf[PATH_MAX]; + pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf)); + + pa_unlock_lockfile(lf, c->autospawn_lock_fd); + c->autospawn_lock_fd = -1; + } +} + +pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { + pa_context *c; + assert(mainloop && name); + + c = pa_xmalloc(sizeof(pa_context)); + c->ref = 1; + c->name = pa_xstrdup(name); + c->mainloop = mainloop; + c->client = NULL; + c->pstream = NULL; + c->pdispatch = NULL; + c->playback_streams = pa_dynarray_new(); + c->record_streams = pa_dynarray_new(); + assert(c->playback_streams && c->record_streams); + + PA_LLIST_HEAD_INIT(pa_stream, c->streams); + PA_LLIST_HEAD_INIT(pa_operation, c->operations); + + c->error = PA_ERROR_OK; + c->state = PA_CONTEXT_UNCONNECTED; + c->ctag = 0; + + c->state_callback = NULL; + c->state_userdata = NULL; + + c->subscribe_callback = NULL; + c->subscribe_userdata = NULL; + + c->memblock_stat = pa_memblock_stat_new(); + c->local = -1; + c->server_list = NULL; + c->server = NULL; + c->autospawn_lock_fd = -1; + memset(&c->spawn_api, 0, sizeof(c->spawn_api)); + c->do_autospawn = 0; + +#ifdef SIGPIPE + pa_check_signal_is_blocked(SIGPIPE); +#endif + + c->conf = pa_client_conf_new(); + pa_client_conf_load(c->conf, NULL); +#ifdef HAVE_X11 + pa_client_conf_from_x11(c->conf, NULL); +#endif + pa_client_conf_env(c->conf); + + return c; +} + +static void context_free(pa_context *c) { + assert(c); + + unlock_autospawn_lock_file(c); + + while (c->operations) + pa_operation_cancel(c->operations); + + while (c->streams) + pa_stream_set_state(c->streams, PA_STREAM_TERMINATED); + + if (c->client) + pa_socket_client_unref(c->client); + if (c->pdispatch) + pa_pdispatch_unref(c->pdispatch); + if (c->pstream) { + pa_pstream_close(c->pstream); + pa_pstream_unref(c->pstream); + } + + if (c->record_streams) + pa_dynarray_free(c->record_streams, NULL, NULL); + if (c->playback_streams) + pa_dynarray_free(c->playback_streams, NULL, NULL); + + pa_memblock_stat_unref(c->memblock_stat); + + if (c->conf) + pa_client_conf_free(c->conf); + + pa_strlist_free(c->server_list); + + pa_xfree(c->name); + pa_xfree(c->server); + pa_xfree(c); +} + +pa_context* pa_context_ref(pa_context *c) { + assert(c && c->ref >= 1); + c->ref++; + return c; +} + +void pa_context_unref(pa_context *c) { + assert(c && c->ref >= 1); + + if ((--(c->ref)) == 0) + context_free(c); +} + +void pa_context_set_state(pa_context *c, pa_context_state_t st) { + assert(c); + + if (c->state == st) + return; + + pa_context_ref(c); + + if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) { + pa_stream *s; + + s = c->streams ? pa_stream_ref(c->streams) : NULL; + while (s) { + pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL; + pa_stream_set_state(s, st == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED); + pa_stream_unref(s); + s = n; + } + + if (c->pdispatch) + pa_pdispatch_unref(c->pdispatch); + c->pdispatch = NULL; + + if (c->pstream) { + pa_pstream_close(c->pstream); + pa_pstream_unref(c->pstream); + } + c->pstream = NULL; + + if (c->client) + pa_socket_client_unref(c->client); + c->client = NULL; + } + + c->state = st; + if (c->state_callback) + c->state_callback(c, c->state_userdata); + + pa_context_unref(c); +} + +void pa_context_fail(pa_context *c, int error) { + assert(c); + c->error = error; + pa_context_set_state(c, PA_CONTEXT_FAILED); +} + +static void pstream_die_callback(pa_pstream *p, void *userdata) { + pa_context *c = userdata; + assert(p && c); + pa_context_fail(c, PA_ERROR_CONNECTIONTERMINATED); +} + +static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) { + pa_context *c = userdata; + assert(p && packet && c); + + pa_context_ref(c); + + if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) { + pa_log(__FILE__": invalid packet.\n"); + pa_context_fail(c, PA_ERROR_PROTOCOL); + } + + pa_context_unref(c); +} + +static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, PA_GCC_UNUSED uint32_t delta, const pa_memchunk *chunk, void *userdata) { + pa_context *c = userdata; + pa_stream *s; + assert(p && chunk && c && chunk->memblock && chunk->memblock->data); + + pa_context_ref(c); + + if ((s = pa_dynarray_get(c->record_streams, channel))) { + pa_mcalign_push(s->mcalign, chunk); + + for (;;) { + pa_memchunk t; + + if (pa_mcalign_pop(s->mcalign, &t) < 0) + break; + + if (s->read_callback) { + s->read_callback(s, (uint8_t*) t.memblock->data + t.index, t.length, s->read_userdata); + s->counter += chunk->length; + } + + pa_memblock_unref(t.memblock); + } + } + + pa_context_unref(c); +} + +int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) { + assert(c); + + if (command == PA_COMMAND_ERROR) { + assert(t); + + if (pa_tagstruct_getu32(t, &c->error) < 0) { + pa_context_fail(c, PA_ERROR_PROTOCOL); + return -1; + + } + } else if (command == PA_COMMAND_TIMEOUT) + c->error = PA_ERROR_TIMEOUT; + else { + pa_context_fail(c, PA_ERROR_PROTOCOL); + return -1; + } + + return 0; +} + +static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_context *c = userdata; + assert(pd && c && (c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME)); + + pa_context_ref(c); + + if (command != PA_COMMAND_REPLY) { + + if (pa_context_handle_error(c, command, t) < 0) + pa_context_fail(c, PA_ERROR_PROTOCOL); + + pa_context_fail(c, c->error); + goto finish; + } + + switch(c->state) { + case PA_CONTEXT_AUTHORIZING: { + pa_tagstruct *reply; + reply = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME); + pa_tagstruct_putu32(reply, tag = c->ctag++); + pa_tagstruct_puts(reply, c->name); + pa_pstream_send_tagstruct(c->pstream, reply); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c); + + pa_context_set_state(c, PA_CONTEXT_SETTING_NAME); + break; + } + + case PA_CONTEXT_SETTING_NAME : + pa_context_set_state(c, PA_CONTEXT_READY); + break; + + default: + assert(0); + } + +finish: + pa_context_unref(c); +} + +static void setup_context(pa_context *c, pa_iochannel *io) { + pa_tagstruct *t; + uint32_t tag; + assert(c && io); + + pa_context_ref(c); + + assert(!c->pstream); + c->pstream = pa_pstream_new(c->mainloop, io, c->memblock_stat); + assert(c->pstream); + + pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c); + pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c); + pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c); + + assert(!c->pdispatch); + c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX); + assert(c->pdispatch); + + if (!c->conf->cookie_valid) { + pa_context_fail(c, PA_ERROR_AUTHKEY); + goto finish; + } + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_AUTH); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_put_arbitrary(t, c->conf->cookie, sizeof(c->conf->cookie)); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c); + + pa_context_set_state(c, PA_CONTEXT_AUTHORIZING); + +finish: + + pa_context_unref(c); +} + +static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata); + +#ifndef OS_IS_WIN32 + +static int context_connect_spawn(pa_context *c) { + pid_t pid; + int status, r; + int fds[2] = { -1, -1} ; + pa_iochannel *io; + + pa_context_ref(c); + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { + pa_log(__FILE__": socketpair() failed: %s\n", strerror(errno)); + pa_context_fail(c, PA_ERROR_INTERNAL); + goto fail; + } + + pa_fd_set_cloexec(fds[0], 1); + + pa_socket_low_delay(fds[0]); + pa_socket_low_delay(fds[1]); + + if (c->spawn_api.prefork) + c->spawn_api.prefork(); + + if ((pid = fork()) < 0) { + pa_log(__FILE__": fork() failed: %s\n", strerror(errno)); + pa_context_fail(c, PA_ERROR_INTERNAL); + + if (c->spawn_api.postfork) + c->spawn_api.postfork(); + + goto fail; + } else if (!pid) { + /* Child */ + + char t[128]; + const char *state = NULL; +#define MAX_ARGS 64 + const char * argv[MAX_ARGS+1]; + int n; + + /* Not required, since fds[0] has CLOEXEC enabled anyway */ + close(fds[0]); + + if (c->spawn_api.atfork) + c->spawn_api.atfork(); + + /* Setup argv */ + + n = 0; + + argv[n++] = c->conf->daemon_binary; + argv[n++] = "--daemonize=yes"; + + snprintf(t, sizeof(t), "-Lmodule-native-protocol-fd fd=%i", fds[1]); + argv[n++] = strdup(t); + + while (n < MAX_ARGS) { + char *a; + + if (!(a = pa_split_spaces(c->conf->extra_arguments, &state))) + break; + + argv[n++] = a; + } + + argv[n++] = NULL; + + execv(argv[0], (char * const *) argv); + _exit(1); +#undef MAX_ARGS + } + + /* Parent */ + + r = waitpid(pid, &status, 0); + + if (c->spawn_api.postfork) + c->spawn_api.postfork(); + + if (r < 0) { + pa_log(__FILE__": waitpid() failed: %s\n", strerror(errno)); + pa_context_fail(c, PA_ERROR_INTERNAL); + goto fail; + } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED); + goto fail; + } + + close(fds[1]); + + c->local = 1; + + io = pa_iochannel_new(c->mainloop, fds[0], fds[0]); + + setup_context(c, io); + unlock_autospawn_lock_file(c); + + pa_context_unref(c); + + return 0; + +fail: + if (fds[0] != -1) + close(fds[0]); + if (fds[1] != -1) + close(fds[1]); + + unlock_autospawn_lock_file(c); + + pa_context_unref(c); + + return -1; +} + +#endif /* OS_IS_WIN32 */ + +static int try_next_connection(pa_context *c) { + char *u = NULL; + int r = -1; + assert(c && !c->client); + + for (;;) { + if (u) + pa_xfree(u); + u = NULL; + + c->server_list = pa_strlist_pop(c->server_list, &u); + + if (!u) { + +#ifndef OS_IS_WIN32 + if (c->do_autospawn) { + r = context_connect_spawn(c); + goto finish; + } +#endif + + pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED); + goto finish; + } + + pa_log_debug(__FILE__": Trying to connect to %s...\n", u); + + pa_xfree(c->server); + c->server = pa_xstrdup(u); + + if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT))) + continue; + + c->local = pa_socket_client_is_local(c->client); + pa_socket_client_set_callback(c->client, on_connection, c); + break; + } + + r = 0; + +finish: + if (u) + pa_xfree(u); + + return r; +} + +static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) { + pa_context *c = userdata; + assert(client && c && c->state == PA_CONTEXT_CONNECTING); + + pa_context_ref(c); + + pa_socket_client_unref(client); + c->client = NULL; + + if (!io) { + /* Try the item in the list */ + if (errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EHOSTUNREACH) { + try_next_connection(c); + goto finish; + } + + pa_context_fail(c, PA_ERROR_CONNECTIONREFUSED); + goto finish; + } + + unlock_autospawn_lock_file(c); + setup_context(c, io); + +finish: + pa_context_unref(c); +} + +int pa_context_connect(pa_context *c, const char *server, int spawn, const pa_spawn_api *api) { + int r = -1; + assert(c && c->ref >= 1 && c->state == PA_CONTEXT_UNCONNECTED); + + if (!server) + server = c->conf->default_server; + + pa_context_ref(c); + + assert(!c->server_list); + + if (server) { + if (!(c->server_list = pa_strlist_parse(server))) { + pa_context_fail(c, PA_ERROR_INVALIDSERVER); + goto finish; + } + } else { + char *d; + char ufn[PATH_MAX]; + + /* Prepend in reverse order */ + + if ((d = getenv("DISPLAY"))) { + char *e; + d = pa_xstrdup(d); + if ((e = strchr(d, ':'))) + *e = 0; + + if (*d) + c->server_list = pa_strlist_prepend(c->server_list, d); + + pa_xfree(d); + } + + c->server_list = pa_strlist_prepend(c->server_list, "tcp6:localhost"); + c->server_list = pa_strlist_prepend(c->server_list, "localhost"); + c->server_list = pa_strlist_prepend(c->server_list, pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET, ufn, sizeof(ufn))); + + /* Wrap the connection attempts in a single transaction for sane autospawn locking */ + if (spawn && c->conf->autospawn) { + char lf[PATH_MAX]; + + pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf)); + pa_make_secure_parent_dir(lf); + assert(c->autospawn_lock_fd <= 0); + c->autospawn_lock_fd = pa_lock_lockfile(lf); + + if (api) + c->spawn_api = *api; + c->do_autospawn = 1; + } + + } + + pa_context_set_state(c, PA_CONTEXT_CONNECTING); + r = try_next_connection(c); + +finish: + pa_context_unref(c); + + return r; +} + +void pa_context_disconnect(pa_context *c) { + assert(c); + pa_context_set_state(c, PA_CONTEXT_TERMINATED); +} + +pa_context_state_t pa_context_get_state(pa_context *c) { + assert(c && c->ref >= 1); + return c->state; +} + +int pa_context_errno(pa_context *c) { + assert(c && c->ref >= 1); + return c->error; +} + +void pa_context_set_state_callback(pa_context *c, void (*cb)(pa_context *c, void *userdata), void *userdata) { + assert(c && c->ref >= 1); + c->state_callback = cb; + c->state_userdata = userdata; +} + +int pa_context_is_pending(pa_context *c) { + assert(c && c->ref >= 1); + +/* pa_log("pstream: %i\n", pa_pstream_is_pending(c->pstream)); */ +/* pa_log("pdispatch: %i\n", pa_pdispatch_is_pending(c->pdispatch)); */ + + return (c->pstream && pa_pstream_is_pending(c->pstream)) || + (c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) || + c->client; +} + +static void set_dispatch_callbacks(pa_operation *o); + +static void pdispatch_drain_callback(PA_GCC_UNUSED pa_pdispatch*pd, void *userdata) { + set_dispatch_callbacks(userdata); +} + +static void pstream_drain_callback(PA_GCC_UNUSED pa_pstream *s, void *userdata) { + set_dispatch_callbacks(userdata); +} + +static void set_dispatch_callbacks(pa_operation *o) { + int done = 1; + assert(o && o->context && o->context->ref >= 1 && o->ref >= 1 && o->context->state == PA_CONTEXT_READY); + + pa_pstream_set_drain_callback(o->context->pstream, NULL, NULL); + pa_pdispatch_set_drain_callback(o->context->pdispatch, NULL, NULL); + + if (pa_pdispatch_is_pending(o->context->pdispatch)) { + pa_pdispatch_set_drain_callback(o->context->pdispatch, pdispatch_drain_callback, o); + done = 0; + } + + if (pa_pstream_is_pending(o->context->pstream)) { + pa_pstream_set_drain_callback(o->context->pstream, pstream_drain_callback, o); + done = 0; + } + + if (!done) + pa_operation_ref(o); + else { + if (o->callback) { + void (*cb)(pa_context *c, void *userdata); + cb = (void (*)(pa_context*, void*)) o->callback; + cb(o->context, o->userdata); + } + + pa_operation_done(o); + } + + pa_operation_unref(o); +} + +pa_operation* pa_context_drain(pa_context *c, void (*cb) (pa_context*c, void *userdata), void *userdata) { + pa_operation *o; + assert(c && c->ref >= 1); + + if (c->state != PA_CONTEXT_READY) + return NULL; + + if (!pa_context_is_pending(c)) + return NULL; + + o = pa_operation_new(c, NULL); + assert(o); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + set_dispatch_callbacks(pa_operation_ref(o)); + + return o; +} + +void pa_context_exit_daemon(pa_context *c) { + pa_tagstruct *t; + assert(c && c->ref >= 1); + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_EXIT); + pa_tagstruct_putu32(t, c->ctag++); + pa_pstream_send_tagstruct(c->pstream, t); +} + +void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int success = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + success = 0; + } else if (!pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *c, int _success, void *_userdata) = (void (*)(pa_context *c, int _success, void *_userdata)) o->callback; + cb(o->context, success, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, command); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_SET_DEFAULT_SINK); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_set_default_source(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_SET_DEFAULT_SOURCE); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); + + return pa_operation_ref(o); +} + +int pa_context_is_local(pa_context *c) { + assert(c); + return c->local; +} + +pa_operation* pa_context_set_name(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && name && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_SET_CLIENT_NAME); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); + + return pa_operation_ref(o); +} + +const char* pa_get_library_version(void) { + return PACKAGE_VERSION; +} + +const char* pa_context_get_server(pa_context *c) { + + if (!c->server) + return NULL; + + if (*c->server == '{') { + char *e = strchr(c->server+1, '}'); + return e ? e+1 : c->server; + } + + return c->server; +} diff --git a/src/polyp/polyplib-context.h b/src/polyp/polyplib-context.h new file mode 100644 index 00000000..febb75f4 --- /dev/null +++ b/src/polyp/polyplib-context.h @@ -0,0 +1,117 @@ +#ifndef foopolyplibcontexthfoo +#define foopolyplibcontexthfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include +#include +#include + +/** \file + * Connection contexts for asynchrononous communication with a + * server. A pa_context object wraps a connection to a polypaudio + * server using its native protocol. A context may be used to issue + * commands on the server or to create playback or recording + * streams. Multiple playback streams may be piped through a single + * connection context. Operations on the contect involving + * communication with the server are executed asynchronously: i.e. the + * client function do not implicitely wait for completion of the + * operation on the server. Instead the caller specifies a call back + * function that is called when the operation is completed. Currently + * running operations may be canceled using pa_operation_cancel(). */ + +/** \example pacat.c + * A playback and recording tool using the asynchronous API */ + +/** \example paplay.c + * A sound file playback tool using the asynchronous API, based on libsndfile */ + +PA_C_DECL_BEGIN + +/** \pa_context + * An opaque connection context to a daemon */ +typedef struct pa_context pa_context; + +/** Instantiate a new connection context with an abstract mainloop API + * and an application name */ +pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name); + +/** Decrease the reference counter of the context by one */ +void pa_context_unref(pa_context *c); + +/** Increase the reference counter of the context by one */ +pa_context* pa_context_ref(pa_context *c); + +typedef void (*pa_context_state_callback)(pa_context *c, void *userdata); + +/** Set a callback function that is called whenever the context status changes */ +void pa_context_set_state_callback(pa_context *c, pa_context_state_callback callback, void *userdata); + +/** Return the error number of the last failed operation */ +int pa_context_errno(pa_context *c); + +/** Return non-zero if some data is pending to be written to the connection */ +int pa_context_is_pending(pa_context *c); + +/** Return the current context status */ +pa_context_state_t pa_context_get_state(pa_context *c); + +/** Connect the context to the specified server. If server is NULL, +connect to the default server. This routine may but will not always +return synchronously on error. Use pa_context_set_state_callback() to +be notified when the connection is established. If spawn is non-zero +and no specific server is specified or accessible a new daemon is +spawned. If api is non-NULL, the functions specified in the structure +are used when forking a new child process. */ +int pa_context_connect(pa_context *c, const char *server, int spawn, const pa_spawn_api *api); + +/** Terminate the context connection immediately */ +void pa_context_disconnect(pa_context *c); + +/** Drain the context. If there is nothing to drain, the function returns NULL */ +pa_operation* pa_context_drain(pa_context *c, void (*cb) (pa_context*c, void *userdata), void *userdata); + +/** Tell the daemon to exit. No operation object is returned as the + * connection is terminated when the daemon quits, thus this operation + * would never complete. */ +void pa_context_exit_daemon(pa_context *c); + +/** Set the name of the default sink. \since 0.4 */ +pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata); + +/** Set the name of the default source. \since 0.4 */ +pa_operation* pa_context_set_default_source(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata); + +/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. \since 0.5 */ +int pa_context_is_local(pa_context *c); + +/** Set a different application name for context on the server. \since 0.5 */ +pa_operation* pa_context_set_name(pa_context *c, const char *name, void(*cb)(pa_context*c, int success, void *userdata), void *userdata); + +/** Return the server name this context is connected to. \since 0.7 */ +const char* pa_context_get_server(pa_context *c); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/polyplib-def.h b/src/polyp/polyplib-def.h new file mode 100644 index 00000000..0591ce6c --- /dev/null +++ b/src/polyp/polyplib-def.h @@ -0,0 +1,213 @@ +#ifndef foopolyplibdefhfoo +#define foopolyplibdefhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include + +#include +#include + +/** \file + * Global definitions */ + +PA_C_DECL_BEGIN + +/** The state of a connection context */ +typedef enum pa_context_state { + PA_CONTEXT_UNCONNECTED, /**< The context hasn't been connected yet */ + PA_CONTEXT_CONNECTING, /**< A connection is being established */ + PA_CONTEXT_AUTHORIZING, /**< The client is authorizing itself to the daemon */ + PA_CONTEXT_SETTING_NAME, /**< The client is passing its application name to the daemon */ + PA_CONTEXT_READY, /**< The connection is established, the context is ready to execute operations */ + PA_CONTEXT_FAILED, /**< The connection failed or was disconnected */ + PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */ +} pa_context_state_t; + +/** The state of a stream */ +typedef enum pa_stream_state { + PA_STREAM_DISCONNECTED, /**< The stream is not yet connected to any sink or source */ + PA_STREAM_CREATING, /**< The stream is being created */ + PA_STREAM_READY, /**< The stream is established, you may pass audio data to it now */ + PA_STREAM_FAILED, /**< An error occured that made the stream invalid */ + PA_STREAM_TERMINATED /**< The stream has been terminated cleanly */ +} pa_stream_state_t; + +/** The state of an operation */ +typedef enum pa_operation_state { + PA_OPERATION_RUNNING, /**< The operation is still running */ + PA_OPERATION_DONE, /**< The operation has been completed */ + PA_OPERATION_CANCELED /**< The operation has been canceled */ +} pa_operation_state_t; + +/** An invalid index */ +#define PA_INVALID_INDEX ((uint32_t) -1) + +/** The direction of a pa_stream object */ +typedef enum pa_stream_direction { + PA_STREAM_NODIRECTION, /**< Invalid direction */ + PA_STREAM_PLAYBACK, /**< Playback stream */ + PA_STREAM_RECORD, /**< Record stream */ + PA_STREAM_UPLOAD /**< Sample upload stream */ +} pa_stream_direction_t; + +/** Some special flags for stream connections. \since 0.6 */ +typedef enum pa_stream_flags { + PA_STREAM_START_CORKED = 1, /**< Create the stream corked, requiring an explicit pa_stream_cork() call to uncork it. */ + PA_STREAM_INTERPOLATE_LATENCY = 2 /**< Interpolate the latency for + * this stream. When enabled, + * you can use + * pa_stream_interpolated_xxx() + * for synchronization. Using + * these functions instead of + * pa_stream_get_latency() has + * the advantage of not + * requiring a whole roundtrip + * for responses. Consider using + * this option when frequently + * requesting latency + * information. This is + * especially useful on long latency + * network connections. */ +} pa_stream_flags_t; + +/** Playback and record buffer metrics */ +typedef struct pa_buffer_attr { + uint32_t maxlength; /**< Maximum length of the buffer */ + uint32_t tlength; /**< Playback only: target length of the buffer. The server tries to assure that at least tlength bytes are always available in the buffer */ + uint32_t prebuf; /**< Playback only: pre-buffering. The server does not start with playback before at least prebug bytes are available in the buffer */ + uint32_t minreq; /**< Playback only: minimum request. The server does not request less than minreq bytes from the client, instead waints until the buffer is free enough to request more bytes at once */ + uint32_t fragsize; /**< Recording only: fragment size. The server sends data in blocks of fragsize bytes size. Large values deminish interactivity with other operations on the connection context but decrease control overhead. */ +} pa_buffer_attr; + +/** Error values as used by pa_context_errno(). Use pa_strerror() to convert these values to human readable strings */ +enum { + PA_ERROR_OK, /**< No error */ + PA_ERROR_ACCESS, /**< Access failure */ + PA_ERROR_COMMAND, /**< Unknown command */ + PA_ERROR_INVALID, /**< Invalid argument */ + PA_ERROR_EXIST, /**< Entity exists */ + PA_ERROR_NOENTITY, /**< No such entity */ + PA_ERROR_CONNECTIONREFUSED, /**< Connection refused */ + PA_ERROR_PROTOCOL, /**< Protocol error */ + PA_ERROR_TIMEOUT, /**< Timeout */ + PA_ERROR_AUTHKEY, /**< No authorization key */ + PA_ERROR_INTERNAL, /**< Internal error */ + PA_ERROR_CONNECTIONTERMINATED, /**< Connection terminated */ + PA_ERROR_KILLED, /**< Entity killed */ + PA_ERROR_INVALIDSERVER, /**< Invalid server */ + PA_ERROR_INITFAILED, /**< Module initialization failed */ + PA_ERROR_MAX /**< Not really an error but the first invalid error code */ +}; + +/** Subscription event mask, as used by pa_context_subscribe() */ +typedef enum pa_subscription_mask { + PA_SUBSCRIPTION_MASK_NULL = 0, /**< No events */ + PA_SUBSCRIPTION_MASK_SINK = 1, /**< Sink events */ + PA_SUBSCRIPTION_MASK_SOURCE = 2, /**< Source events */ + PA_SUBSCRIPTION_MASK_SINK_INPUT = 4, /**< Sink input events */ + PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT = 8, /**< Source output events */ + PA_SUBSCRIPTION_MASK_MODULE = 16, /**< Module events */ + PA_SUBSCRIPTION_MASK_CLIENT = 32, /**< Client events */ + PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 64, /**< Sample cache events */ + PA_SUBSCRIPTION_MASK_SERVER = 128, /**< Other global server changes. \since 0.4 */ + PA_SUBSCRIPTION_MASK_AUTOLOAD = 256 /**< Autoload table events. \since 0.5 */ +} pa_subscription_mask_t; + +/** Subscription event types, as used by pa_context_subscribe() */ +typedef enum pa_subscription_event_type { + PA_SUBSCRIPTION_EVENT_SINK = 0, /**< Event type: Sink */ + PA_SUBSCRIPTION_EVENT_SOURCE = 1, /**< Event type: Source */ + PA_SUBSCRIPTION_EVENT_SINK_INPUT = 2, /**< Event type: Sink input */ + PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT = 3, /**< Event type: Source output */ + PA_SUBSCRIPTION_EVENT_MODULE = 4, /**< Event type: Module */ + PA_SUBSCRIPTION_EVENT_CLIENT = 5, /**< Event type: Client */ + PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 6, /**< Event type: Sample cache item */ + PA_SUBSCRIPTION_EVENT_SERVER = 7, /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. \since 0.4 */ + PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8, /**< Event type: Autoload table changes. \since 0.5 */ + PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 15, /**< A mask to extract the event type from an event value */ + + PA_SUBSCRIPTION_EVENT_NEW = 0, /**< A new object was created */ + PA_SUBSCRIPTION_EVENT_CHANGE = 16, /**< A property of the object was modified */ + PA_SUBSCRIPTION_EVENT_REMOVE = 32, /**< An object was removed */ + PA_SUBSCRIPTION_EVENT_TYPE_MASK = 16+32 /**< A mask to extract the event operation from an event value */ +} pa_subscription_event_type_t; + +/** Return one if an event type t matches an event mask bitfield */ +#define pa_subscription_match_flags(m, t) (!!((m) & (1 << ((t) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)))) + +/** A structure for latency info. See pa_stream_get_latency(). The + * total output latency a sample that is written with + * pa_stream_write() takes to be played may be estimated by + * sink_usec+buffer_usec+transport_usec. The output buffer to which + * buffer_usec relates may be manipulated freely (with + * pa_stream_write()'s delta argument, pa_stream_flush() and friends), + * the buffers sink_usec/source_usec relates to is a first-in + * first-out buffer which cannot be flushed or manipulated in any + * way. The total input latency a sample that is recorded takes to be + * delivered to the application is: + * source_usec+buffer_usec+transport_usec-sink_usec. (Take care of + * sign issues!) When connected to a monitor source sink_usec contains + * the latency of the owning sink.*/ +typedef struct pa_latency_info { + pa_usec_t buffer_usec; /**< Time in usecs the current buffer takes to play. For both playback and record streams. */ + pa_usec_t sink_usec; /**< Time in usecs a sample takes to be played on the sink. For playback streams and record streams connected to a monitor source. */ + pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. \since 0.5*/ + pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. \since 0.5 */ + int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */ + uint32_t queue_length; /**< Queue size in bytes. For both playback and record streams. */ + int synchronized_clocks; /**< Non-zero if the local and the + * remote machine have synchronized + * clocks. If synchronized clocks are + * detected transport_usec becomes much + * more reliable. However, the code that + * detects synchronized clocks is very + * limited und unreliable itself. \since + * 0.5 */ + struct timeval timestamp; /**< The time when this latency info was current */ + uint64_t counter; /**< The byte counter current when the latency info was requested. \since 0.6 */ +} pa_latency_info; + +/** A structure for the spawn api. This may be used to integrate auto + * spawned daemons into your application. For more information see + * pa_context_connect(). When spawning a new child process the + * waitpid() is used on the child's PID. The spawn routine will not + * block or ignore SIGCHLD signals, since this cannot be done in a + * thread compatible way. You might have to do this in + * prefork/postfork. \since 0.4 */ +typedef struct pa_spawn_api { + void (*prefork)(void); /**< Is called just before the fork in the parent process. May be NULL. */ + void (*postfork)(void); /**< Is called immediately after the fork in the parent process. May be NULL.*/ + void (*atfork)(void); /**< Is called immediately after the + * fork in the child process. May be + * NULL. It is not safe to close all + * file descriptors in this function + * unconditionally, since a UNIX socket + * (created using socketpair()) is + * passed to the new process. */ +} pa_spawn_api; + +PA_C_DECL_END + +#endif diff --git a/src/polyp/polyplib-error.c b/src/polyp/polyplib-error.c new file mode 100644 index 00000000..188d6a93 --- /dev/null +++ b/src/polyp/polyplib-error.c @@ -0,0 +1,54 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "polyplib-error.h" +#include + +static const char* const errortab[PA_ERROR_MAX] = { + [PA_ERROR_OK] = "OK", + [PA_ERROR_ACCESS] = "Access denied", + [PA_ERROR_COMMAND] = "Unknown command", + [PA_ERROR_INVALID] = "Invalid argument", + [PA_ERROR_EXIST] = "Entity exists", + [PA_ERROR_NOENTITY] = "No such entity", + [PA_ERROR_CONNECTIONREFUSED] = "Connection refused", + [PA_ERROR_PROTOCOL] = "Protocol error", + [PA_ERROR_TIMEOUT] = "Timeout", + [PA_ERROR_AUTHKEY] = "No authorization key", + [PA_ERROR_INTERNAL] = "Internal error", + [PA_ERROR_CONNECTIONTERMINATED] = "Connection terminated", + [PA_ERROR_KILLED] = "Entity killed", + [PA_ERROR_INVALIDSERVER] = "Invalid server", +}; + +const char*pa_strerror(uint32_t error) { + if (error >= PA_ERROR_MAX) + return NULL; + + return errortab[error]; +} diff --git a/src/polyp/polyplib-error.h b/src/polyp/polyplib-error.h new file mode 100644 index 00000000..1bb97822 --- /dev/null +++ b/src/polyp/polyplib-error.h @@ -0,0 +1,38 @@ +#ifndef foopolypliberrorhfoo +#define foopolypliberrorhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +/** \file + * Error management */ + +PA_C_DECL_BEGIN + +/** Return a human readable error message for the specified numeric error code */ +const char* pa_strerror(uint32_t error); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/polyplib-internal.h b/src/polyp/polyplib-internal.h new file mode 100644 index 00000000..b95a20f3 --- /dev/null +++ b/src/polyp/polyplib-internal.h @@ -0,0 +1,154 @@ +#ifndef foopolyplibinternalhfoo +#define foopolyplibinternalhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include +#include +#include + +#include "polyplib-context.h" +#include "polyplib-stream.h" +#include "polyplib-operation.h" +#include +#include +#include +#include +#include + +#define DEFAULT_TIMEOUT (10) + +struct pa_context { + int ref; + + char *name; + pa_mainloop_api* mainloop; + + pa_socket_client *client; + pa_pstream *pstream; + pa_pdispatch *pdispatch; + + pa_dynarray *record_streams, *playback_streams; + PA_LLIST_HEAD(pa_stream, streams); + PA_LLIST_HEAD(pa_operation, operations); + + uint32_t ctag; + uint32_t error; + pa_context_state_t state; + + void (*state_callback)(pa_context*c, void *userdata); + void *state_userdata; + + void (*subscribe_callback)(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata); + void *subscribe_userdata; + + pa_memblock_stat *memblock_stat; + + int local; + int do_autospawn; + int autospawn_lock_fd; + pa_spawn_api spawn_api; + + pa_strlist *server_list; + + char *server; + + pa_client_conf *conf; +}; + +struct pa_stream { + int ref; + pa_context *context; + pa_mainloop_api *mainloop; + PA_LLIST_FIELDS(pa_stream); + + char *name; + pa_buffer_attr buffer_attr; + pa_sample_spec sample_spec; + pa_channel_map channel_map; + uint32_t channel; + int channel_valid; + uint32_t device_index; + pa_stream_direction_t direction; + uint32_t requested_bytes; + uint64_t counter; + pa_usec_t previous_time; + pa_usec_t previous_ipol_time; + pa_stream_state_t state; + pa_mcalign *mcalign; + + int interpolate; + int corked; + + uint32_t ipol_usec; + struct timeval ipol_timestamp; + pa_time_event *ipol_event; + int ipol_requested; + + void (*state_callback)(pa_stream*c, void *userdata); + void *state_userdata; + + void (*read_callback)(pa_stream *p, const void*data, size_t length, void *userdata); + void *read_userdata; + + void (*write_callback)(pa_stream *p, size_t length, void *userdata); + void *write_userdata; +}; + +typedef void (*pa_operation_callback)(void); + +struct pa_operation { + int ref; + pa_context *context; + pa_stream *stream; + PA_LLIST_FIELDS(pa_operation); + + pa_operation_state_t state; + void *userdata; + pa_operation_callback callback; +}; + +void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); + +pa_operation *pa_operation_new(pa_context *c, pa_stream *s); +void pa_operation_done(pa_operation *o); + +void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); + +void pa_context_fail(pa_context *c, int error); +void pa_context_set_state(pa_context *c, pa_context_state_t st); +int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t); +pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata); + +void pa_stream_set_state(pa_stream *s, pa_stream_state_t st); + +void pa_stream_trash_ipol(pa_stream *s); + + +#endif diff --git a/src/polyp/polyplib-introspect.c b/src/polyp/polyplib-introspect.c new file mode 100644 index 00000000..0bdffa35 --- /dev/null +++ b/src/polyp/polyplib-introspect.c @@ -0,0 +1,1003 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "polyplib-introspect.h" +#include "polyplib-context.h" +#include "polyplib-internal.h" +#include +#include + +/*** Statistics ***/ + +static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + pa_stat_info i, *p = &i; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + p = NULL; + } else if (pa_tagstruct_getu32(t, &i.memblock_total) < 0 || + pa_tagstruct_getu32(t, &i.memblock_total_size) < 0 || + pa_tagstruct_getu32(t, &i.memblock_allocated) < 0 || + pa_tagstruct_getu32(t, &i.memblock_allocated_size) < 0 || + pa_tagstruct_getu32(t, &i.scache_size) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_stat_info*_i, void *_userdata) = (void (*)(pa_context *s, const pa_stat_info*_i, void *_userdata)) o->callback; + cb(o->context, p, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_stat(pa_context *c, void (*cb)(pa_context *c, const pa_stat_info*i, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_STAT, context_stat_callback, (pa_operation_callback) cb, userdata); +} + +/*** Server Info ***/ + +static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + pa_server_info i, *p = &i; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + p = NULL; + } else if (pa_tagstruct_gets(t, &i.server_name) < 0 || + pa_tagstruct_gets(t, &i.server_version) < 0 || + pa_tagstruct_gets(t, &i.user_name) < 0 || + pa_tagstruct_gets(t, &i.host_name) < 0 || + pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || + pa_tagstruct_gets(t, &i.default_sink_name) < 0 || + pa_tagstruct_gets(t, &i.default_source_name) < 0 || + pa_tagstruct_getu32(t, &i.cookie) < 0 || + !pa_tagstruct_eof(t)) { + + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_server_info*_i, void *_userdata) = (void (*)(pa_context *s, const pa_server_info*_i, void *_userdata)) o->callback; + cb(o->context, p, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_server_info(pa_context *c, void (*cb)(pa_context *c, const pa_server_info*i, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SERVER_INFO, context_get_server_info_callback, (pa_operation_callback) cb, userdata); +} + +/*** Sink Info ***/ + +static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eof = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eof = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_sink_info i; + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_gets(t, &i.description) < 0 || + pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || + pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || + pa_tagstruct_getu32(t, &i.owner_module) < 0 || + pa_tagstruct_get_cvolume(t, &i.volume) < 0 || + pa_tagstruct_getu32(t, &i.monitor_source) < 0 || + pa_tagstruct_gets(t, &i.monitor_source_name) < 0 || + pa_tagstruct_get_usec(t, &i.latency) < 0 || + pa_tagstruct_gets(t, &i.driver) < 0) { + + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, NULL, eof, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_sink_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INFO_LIST, context_get_sink_info_callback, (pa_operation_callback) cb, userdata); +} + +pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, NULL); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_info_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_info_callback, o); + + return pa_operation_ref(o); +} + +/*** Source info ***/ + +static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eof = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eof = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_source_info i; + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_gets(t, &i.description) < 0 || + pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || + pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || + pa_tagstruct_getu32(t, &i.owner_module) < 0 || + pa_tagstruct_getu32(t, &i.monitor_of_sink) < 0 || + pa_tagstruct_gets(t, &i.monitor_of_sink_name) < 0 || + pa_tagstruct_get_usec(t, &i.latency) < 0 || + pa_tagstruct_gets(t, &i.driver) < 0) { + + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, NULL, eof, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_source_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_INFO_LIST, context_get_source_info_callback, (pa_operation_callback) cb, userdata); +} + +pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, NULL); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_info_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_info_callback, o); + + return pa_operation_ref(o); +} + +/*** Client info ***/ + +static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eof = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eof = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_client_info i; + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_getu32(t, &i.owner_module) < 0 || + pa_tagstruct_gets(t, &i.driver) < 0 ) { + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_client_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, NULL, eof, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_CLIENT_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, idx); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_client_info_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_client_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_CLIENT_INFO_LIST, context_get_client_info_callback, (pa_operation_callback) cb, userdata); +} + +/*** Module info ***/ + +static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eof = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eof = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_module_info i; + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_gets(t, &i.argument) < 0 || + pa_tagstruct_getu32(t, &i.n_used) < 0 || + pa_tagstruct_get_boolean(t, &i.auto_unload) < 0) { + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_module_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, NULL, eof, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_MODULE_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, idx); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_module_info_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_module_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_MODULE_INFO_LIST, context_get_module_info_callback, (pa_operation_callback) cb, userdata); +} + +/*** Sink input info ***/ + +static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eof = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eof = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_sink_input_info i; + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_getu32(t, &i.owner_module) < 0 || + pa_tagstruct_getu32(t, &i.client) < 0 || + pa_tagstruct_getu32(t, &i.sink) < 0 || + pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || + pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || + pa_tagstruct_get_cvolume(t, &i.volume) < 0 || + pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 || + pa_tagstruct_get_usec(t, &i.sink_usec) < 0 || + pa_tagstruct_gets(t, &i.resample_method) < 0 || + pa_tagstruct_gets(t, &i.driver) < 0) { + + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sink_input_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, NULL, eof, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INPUT_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, idx); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sink_input_info_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_sink_input_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SINK_INPUT_INFO_LIST, context_get_sink_input_info_callback, (pa_operation_callback) cb, userdata); +} + +/*** Source output info ***/ + +static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eof = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eof = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_source_output_info i; + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_getu32(t, &i.owner_module) < 0 || + pa_tagstruct_getu32(t, &i.client) < 0 || + pa_tagstruct_getu32(t, &i.source) < 0 || + pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || + pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || + pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 || + pa_tagstruct_get_usec(t, &i.source_usec) < 0 || + pa_tagstruct_gets(t, &i.resample_method) < 0 || + pa_tagstruct_gets(t, &i.driver) < 0) { + + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_source_output_info*_i, int _eof, void *_userdata))o->callback; + cb(o->context, NULL, eof, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_OUTPUT_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, idx); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_source_output_info_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_source_output_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST, context_get_source_output_info_callback, (pa_operation_callback) cb, userdata); +} + +/*** Volume manipulation ***/ + +pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && idx != PA_INVALID_INDEX); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_VOLUME); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, NULL); + pa_tagstruct_put_cvolume(t, volume); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && name); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_VOLUME); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, name); + pa_tagstruct_put_cvolume(t, volume); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && idx != PA_INVALID_INDEX); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_put_cvolume(t, volume); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); + + return pa_operation_ref(o); +} + +/** Sample Cache **/ + +static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eof = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eof = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_sample_info i; + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_get_cvolume(t, &i.volume) < 0 || + pa_tagstruct_get_usec(t, &i.duration) < 0 || + pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || + pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || + pa_tagstruct_getu32(t, &i.bytes) < 0 || + pa_tagstruct_get_boolean(t, &i.lazy) < 0 || + pa_tagstruct_gets(t, &i.filename) < 0) { + + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_sample_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, NULL, eof, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb && name); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SAMPLE_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sample_info_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_SAMPLE_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, idx); + pa_tagstruct_puts(t, NULL); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_sample_info_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_sample_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_SAMPLE_INFO_LIST, context_get_sample_info_callback, (pa_operation_callback) cb, userdata); +} + +static pa_operation* command_kill(pa_context *c, uint32_t command, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && idx != PA_INVALID_INDEX); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, command); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, idx); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + return command_kill(c, PA_COMMAND_KILL_CLIENT, idx, cb, userdata); +} + +pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + return command_kill(c, PA_COMMAND_KILL_SINK_INPUT, idx, cb, userdata); +} + +pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + return command_kill(c, PA_COMMAND_KILL_SOURCE_OUTPUT, idx, cb, userdata); +} + +static void load_module_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + uint32_t idx = -1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + } else if (pa_tagstruct_getu32(t, &idx) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *c, uint32_t _idx, void *_userdata) = (void (*)(pa_context *c, uint32_t _idx, void *_userdata)) o->callback; + cb(o->context, idx, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, void (*cb)(pa_context *c, uint32_t idx, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && name && argument); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_LOAD_MODULE); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_puts(t, name); + pa_tagstruct_puts(t, argument); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, load_module_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + return command_kill(c, PA_COMMAND_UNLOAD_MODULE, idx, cb, userdata); +} + +/*** Autoload stuff ***/ + +static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eof = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + eof = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_autoload_info i; + + if (pa_tagstruct_getu32(t, &i.index) < 0 || + pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_getu32(t, &i.type) < 0 || + pa_tagstruct_gets(t, &i.module) < 0 || + pa_tagstruct_gets(t, &i.argument) < 0) { + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + void (*cb)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata) = (void (*)(pa_context *s, const pa_autoload_info*_i, int _eof, void *_userdata)) o->callback; + cb(o->context, NULL, eof, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb && name); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_AUTOLOAD_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_puts(t, name); + pa_tagstruct_putu32(t, type); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(c && cb && idx != PA_INVALID_INDEX); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_GET_AUTOLOAD_INFO); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, idx); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_get_autoload_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata) { + return pa_context_send_simple_command(c, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, context_get_autoload_info_callback, (pa_operation_callback) cb, userdata); +} + +static void context_add_autoload_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + uint32_t idx; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + idx = PA_INVALID_INDEX; + } else if (pa_tagstruct_getu32(t, &idx) || + !pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_context *s, uint32_t _idx, void *_userdata) = (void (*)(pa_context *s, uint32_t _idx, void *_userdata)) o->callback; + cb(o->context, idx, o->userdata); + } + + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, void (*cb)(pa_context *c, int success, void *userdata), void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && name && module && argument); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_ADD_AUTOLOAD); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_puts(t, name); + pa_tagstruct_putu32(t, type); + pa_tagstruct_puts(t, module); + pa_tagstruct_puts(t, argument); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_add_autoload_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, void (*cb)(pa_context *c, int success, void *userdata), void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && name); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_AUTOLOAD); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_puts(t, name); + pa_tagstruct_putu32(t, type); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void* userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && idx != PA_INVALID_INDEX); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_AUTOLOAD); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, idx); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); + + return pa_operation_ref(o); +} diff --git a/src/polyp/polyplib-introspect.h b/src/polyp/polyplib-introspect.h new file mode 100644 index 00000000..d3489908 --- /dev/null +++ b/src/polyp/polyplib-introspect.h @@ -0,0 +1,279 @@ +#ifndef foopolyplibintrospecthfoo +#define foopolyplibintrospecthfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include +#include +#include +#include + +/** \file + * + * Routines for daemon introspection. When enumerating all entitites + * of a certain kind, use the pa_context_xxx_list() functions. The + * specified callback function is called once for each entry. The + * enumeration is finished by a call to the callback function with + * is_last=1 and i=NULL. Strings referenced in pa_xxx_info structures + * and the structures themselves point to internal memory that may not + * be modified. That memory is only valid during the call to the + * callback function. A deep copy is required if you need this data + * outside the callback functions. An error is signalled by a call to * the callback function with i=NULL and is_last=0. + * + * When using the routines that ask fo a single entry only, a callback + * with the same signature is used. However, no finishing call to the + * routine is issued. */ + +PA_C_DECL_BEGIN + +/** Stores information about sinks */ +typedef struct pa_sink_info { + const char *name; /**< Name of the sink */ + uint32_t index; /**< Index of the sink */ + const char *description; /**< Description of this sink */ + pa_sample_spec sample_spec; /**< Sample spec of this sink */ + pa_channel_map channel_map; /**< Channel map \since 0.9 */ + uint32_t owner_module; /**< Index of the owning module of this sink, or PA_INVALID_INDEX */ + pa_cvolume volume; /**< Volume of the sink */ + uint32_t monitor_source; /**< Index of the monitor source connected to this sink */ + const char *monitor_source_name; /**< The name of the monitor source */ + pa_usec_t latency; /**< Length of filled playback buffer of this sink */ + const char *driver; /**< Driver name. \since 0.9 */ +} pa_sink_info; + +/** Get information about a sink by its name */ +pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata); + +/** Get information about a sink by its index */ +pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t id, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata); + +/** Get the complete sink list */ +pa_operation* pa_context_get_sink_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_info *i, int is_last, void *userdata), void *userdata); + +/** Stores information about sources */ +typedef struct pa_source_info { + const char *name ; /**< Name of the source */ + uint32_t index; /**< Index of the source */ + const char *description; /**< Description of this source */ + pa_sample_spec sample_spec; /**< Sample spec of this source */ + pa_channel_map channel_map; /**< Channel map \since 0.9 */ + uint32_t owner_module; /**< Owning module index, or PA_INVALID_INDEX */ + uint32_t monitor_of_sink; /**< If this is a monitor source the index of the owning sink, otherwise PA_INVALID_INDEX */ + const char *monitor_of_sink_name; /**< Name of the owning sink, or PA_INVALID_INDEX */ + pa_usec_t latency; /**< Length of filled record buffer of this source. \since 0.5 */ + const char *driver; /**< Driver name \since 0.9 */ +} pa_source_info; + +/** Get information about a source by its name */ +pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata); + +/** Get information about a source by its index */ +pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t id, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata); + +/** Get the complete source list */ +pa_operation* pa_context_get_source_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_info *i, int is_last, void *userdata), void *userdata); + +/** Server information */ +typedef struct pa_server_info { + const char *user_name; /**< User name of the daemon process */ + const char *host_name; /**< Host name the daemon is running on */ + const char *server_version; /**< Version string of the daemon */ + const char *server_name; /**< Server package name (usually "polypaudio") */ + pa_sample_spec sample_spec; /**< Default sample specification */ + const char *default_sink_name; /**< Name of default sink. \since 0.4 */ + const char *default_source_name; /**< Name of default sink. \since 0.4*/ + uint32_t cookie; /**< A random cookie for identifying this instance of polypaudio. \since 0.8 */ +} pa_server_info; + +/** Get some information about the server */ +pa_operation* pa_context_get_server_info(pa_context *c, void (*cb)(pa_context *c, const pa_server_info*i, void *userdata), void *userdata); + +/** Stores information about modules */ +typedef struct pa_module_info { + uint32_t index; /**< Index of the module */ + const char*name, /**< Name of the module */ + *argument; /**< Argument string of the module */ + uint32_t n_used; /**< Usage counter or PA_INVALID_INDEX */ + int auto_unload; /**< Non-zero if this is an autoloaded module */ +} pa_module_info; + +/** Get some information about a module by its index */ +pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata); + +/** Get the complete list of currently loaded modules */ +pa_operation* pa_context_get_module_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_module_info*i, int is_last, void *userdata), void *userdata); + +/** Stores information about clients */ +typedef struct pa_client_info { + uint32_t index; /**< Index of this client */ + const char *name; /**< Name of this client */ + uint32_t owner_module; /**< Index of the owning module, or PA_INVALID_INDEX */ + const char *driver; /**< Driver name \since 0.9 */ +} pa_client_info; + +/** Get information about a client by its index */ +pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata); + +/** Get the complete client list */ +pa_operation* pa_context_get_client_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_client_info*i, int is_last, void *userdata), void *userdata); + +/** Stores information about sink inputs */ +typedef struct pa_sink_input_info { + uint32_t index; /**< Index of the sink input */ + const char *name; /**< Name of the sink input */ + uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */ + uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */ + uint32_t sink; /**< Index of the connected sink */ + pa_sample_spec sample_spec; /**< The sample specification of the sink input */ + pa_channel_map channel_map; /**< Channel map */ + pa_cvolume volume; /**< The volume of this sink input */ + pa_usec_t buffer_usec; /**< Latency due to buffering in sink input, see pa_latency_info for details */ + pa_usec_t sink_usec; /**< Latency of the sink device, see pa_latency_info for details */ + const char *resample_method; /**< Thre resampling method used by this sink input. \since 0.7 */ + const char *driver; /**< Driver name \since 0.9 */ +} pa_sink_input_info; + +/** Get some information about a sink input by its index */ +pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata); + +/** Get the complete sink input list */ +pa_operation* pa_context_get_sink_input_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sink_input_info*i, int is_last, void *userdata), void *userdata); + +/** Stores information about source outputs */ +typedef struct pa_source_output_info { + uint32_t index; /**< Index of the sink input */ + const char *name; /**< Name of the sink input */ + uint32_t owner_module; /**< Index of the module this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any module */ + uint32_t client; /**< Index of the client this sink input belongs to, or PA_INVALID_INDEX when it does not belong to any client */ + uint32_t source; /**< Index of the connected source */ + pa_sample_spec sample_spec; /**< The sample specification of the source output */ + pa_channel_map channel_map; /**< Channel map */ + pa_usec_t buffer_usec; /**< Latency due to buffering in the source output, see pa_latency_info for details. \since 0.5 */ + pa_usec_t source_usec; /**< Latency of the source device, see pa_latency_info for details. \since 0.5 */ + const char *resample_method; /**< Thre resampling method used by this source output. \since 0.7 */ + const char *driver; /**< Driver name \since 0.9 */ +} pa_source_output_info; + +/** Get information about a source output by its index */ +pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata); + +/** Get the complete list of source outputs */ +pa_operation* pa_context_get_source_output_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_source_output_info*i, int is_last, void *userdata), void *userdata); + +/** Set the volume of a sink device specified by its index */ +pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); + +/** Set the volume of a sink device specified by its name */ +pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); + +/** Set the volume of a sink input stream */ +pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); + +/** Memory block statistics */ +typedef struct pa_stat_info { + uint32_t memblock_total; /**< Currently allocated memory blocks */ + uint32_t memblock_total_size; /**< Currentl total size of allocated memory blocks */ + uint32_t memblock_allocated; /**< Allocated memory blocks during the whole lifetime of the daemon */ + uint32_t memblock_allocated_size; /**< Total size of all memory blocks allocated during the whole lifetime of the daemon */ + uint32_t scache_size; /**< Total size of all sample cache entries. \since 0.4 */ +} pa_stat_info; + +/** Get daemon memory block statistics */ +pa_operation* pa_context_stat(pa_context *c, void (*cb)(pa_context *c, const pa_stat_info *i, void *userdata), void *userdata); + +/** Stores information about sample cache entries */ +typedef struct pa_sample_info { + uint32_t index; /**< Index of this entry */ + const char *name; /**< Name of this entry */ + pa_cvolume volume; /**< Default volume of this entry */ + pa_sample_spec sample_spec; /**< Sample specification of the sample */ + pa_channel_map channel_map; /**< The channel map */ + pa_usec_t duration; /**< Duration of this entry */ + uint32_t bytes; /**< Length of this sample in bytes. \since 0.4 */ + int lazy; /**< Non-zero when this is a lazy cache entry. \since 0.5 */ + const char *filename; /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. \since 0.5 */ +} pa_sample_info; + +/** Get information about a sample by its name */ +pa_operation* pa_context_get_sample_info_by_name(pa_context *c, const char *name, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata); + +/** Get information about a sample by its index */ +pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata); + +/** Get the complete list of samples stored in the daemon. */ +pa_operation* pa_context_get_sample_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_sample_info *i, int is_last, void *userdata), void *userdata); + +/** Kill a client. \since 0.5 */ +pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); + +/** Kill a sink input. \since 0.5 */ +pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); + +/** Kill a source output. \since 0.5 */ +pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); + +/** Load a module. \since 0.5 */ +pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, void (*cb)(pa_context *c, uint32_t idx, void *userdata), void *userdata); + +/** Unload a module. \since 0.5 */ +pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); + +/** Type of an autoload entry. \since 0.5 */ +typedef enum pa_autoload_type { + PA_AUTOLOAD_SINK = 0, + PA_AUTOLOAD_SOURCE = 1 +} pa_autoload_type_t; + +/** Stores information about autoload entries. \since 0.5 */ +typedef struct pa_autoload_info { + uint32_t index; /**< Index of this autoload entry */ + const char *name; /**< Name of the sink or source */ + pa_autoload_type_t type; /**< Type of the autoload entry */ + const char *module; /**< Module name to load */ + const char *argument; /**< Argument string for module */ +} pa_autoload_info; + +/** Get info about a specific autoload entry. \since 0.6 */ +pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata); + +/** Get info about a specific autoload entry. \since 0.6 */ +pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata); + +/** Get the complete list of autoload entries. \since 0.5 */ +pa_operation* pa_context_get_autoload_info_list(pa_context *c, void (*cb)(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata), void *userdata); + +/** Add a new autoload entry. \since 0.5 */ +pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, void (*cb)(pa_context *c, int idx, void *userdata), void* userdata); + +/** Remove an autoload entry. \since 0.6 */ +pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, void (*cb)(pa_context *c, int success, void *userdata), void* userdata); + +/** Remove an autoload entry. \since 0.6 */ +pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, void (*cb)(pa_context *c, int success, void *userdata), void* userdata); + + +PA_C_DECL_END + +#endif diff --git a/src/polyp/polyplib-operation.c b/src/polyp/polyplib-operation.c new file mode 100644 index 00000000..ea336c17 --- /dev/null +++ b/src/polyp/polyplib-operation.c @@ -0,0 +1,103 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include "polyplib-internal.h" +#include "polyplib-operation.h" + +pa_operation *pa_operation_new(pa_context *c, pa_stream *s) { + pa_operation *o; + assert(c); + + o = pa_xmalloc(sizeof(pa_operation)); + o->ref = 1; + o->context = pa_context_ref(c); + o->stream = s ? pa_stream_ref(s) : NULL; + + o->state = PA_OPERATION_RUNNING; + o->userdata = NULL; + o->callback = NULL; + + PA_LLIST_PREPEND(pa_operation, o->context->operations, o); + return pa_operation_ref(o); +} + +pa_operation *pa_operation_ref(pa_operation *o) { + assert(o && o->ref >= 1); + o->ref++; + return o; +} + +void pa_operation_unref(pa_operation *o) { + assert(o && o->ref >= 1); + + if ((--(o->ref)) == 0) { + assert(!o->context); + assert(!o->stream); + free(o); + } +} + +static void operation_set_state(pa_operation *o, pa_operation_state_t st) { + assert(o && o->ref >= 1); + + if (st == o->state) + return; + + if (!o->context) + return; + + o->state = st; + + if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) { + PA_LLIST_REMOVE(pa_operation, o->context->operations, o); + pa_context_unref(o->context); + if (o->stream) + pa_stream_unref(o->stream); + o->context = NULL; + o->stream = NULL; + o->callback = NULL; + o->userdata = NULL; + + pa_operation_unref(o); + } +} + +void pa_operation_cancel(pa_operation *o) { + assert(o && o->ref >= 1); + operation_set_state(o, PA_OPERATION_CANCELED); +} + +void pa_operation_done(pa_operation *o) { + assert(o && o->ref >= 1); + operation_set_state(o, PA_OPERATION_DONE); +} + +pa_operation_state_t pa_operation_get_state(pa_operation *o) { + assert(o && o->ref >= 1); + return o->state; +} diff --git a/src/polyp/polyplib-operation.h b/src/polyp/polyplib-operation.h new file mode 100644 index 00000000..cac03e30 --- /dev/null +++ b/src/polyp/polyplib-operation.h @@ -0,0 +1,51 @@ +#ifndef foopolypliboperationhfoo +#define foopolypliboperationhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +/** \file + * Asynchronous operations */ + +PA_C_DECL_BEGIN + +/** \pa_operation + * An asynchronous operation object */ +typedef struct pa_operation pa_operation; + +/** Increase the reference count by one */ +pa_operation *pa_operation_ref(pa_operation *o); + +/** Decrease the reference count by one */ +void pa_operation_unref(pa_operation *o); + +/** Cancel the operation. Beware! This will not necessarily cancel the execution of the operation on the server side. */ +void pa_operation_cancel(pa_operation *o); + +/** Return the current status of the operation */ +pa_operation_state_t pa_operation_get_state(pa_operation *o); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/polyplib-scache.c b/src/polyp/polyplib-scache.c new file mode 100644 index 00000000..1315af97 --- /dev/null +++ b/src/polyp/polyplib-scache.c @@ -0,0 +1,127 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "polyplib-scache.h" +#include "polyplib-internal.h" +#include + +void pa_stream_connect_upload(pa_stream *s, size_t length) { + pa_tagstruct *t; + uint32_t tag; + + assert(s && length); + + pa_stream_ref(s); + + s->state = PA_STREAM_CREATING; + s->direction = PA_STREAM_UPLOAD; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_CREATE_UPLOAD_STREAM); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + pa_tagstruct_puts(t, s->name); + pa_tagstruct_put_sample_spec(t, &s->sample_spec); + pa_tagstruct_putu32(t, length); + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s); + + pa_stream_unref(s); +} + +void pa_stream_finish_upload(pa_stream *s) { + pa_tagstruct *t; + uint32_t tag; + assert(s); + + if (!s->channel_valid || !s->context->state == PA_CONTEXT_READY) + return; + + pa_stream_ref(s); + + t = pa_tagstruct_new(NULL, 0); + assert(t); + + pa_tagstruct_putu32(t, PA_COMMAND_FINISH_UPLOAD_STREAM); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + pa_tagstruct_putu32(t, s->channel); + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s); + + pa_stream_unref(s); +} + +pa_operation * pa_context_play_sample(pa_context *c, const char *name, const char *dev, uint32_t volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && name && *name && (!dev || *dev)); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + if (!dev) + dev = c->conf->default_sink; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_PLAY_SAMPLE); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, (uint32_t) -1); + pa_tagstruct_puts(t, dev); + pa_tagstruct_putu32(t, volume); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_context_remove_sample(pa_context *c, const char *name, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c && name); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_REMOVE_SAMPLE); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); + + return pa_operation_ref(o); +} + diff --git a/src/polyp/polyplib-scache.h b/src/polyp/polyplib-scache.h new file mode 100644 index 00000000..89d27597 --- /dev/null +++ b/src/polyp/polyplib-scache.h @@ -0,0 +1,50 @@ +#ifndef foopolyplibscachehfoo +#define foopolyplibscachehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include +#include + +/** \file + * All sample cache related routines */ + +PA_C_DECL_BEGIN + +/** Make this stream a sample upload stream */ +void pa_stream_connect_upload(pa_stream *s, size_t length); + +/** Finish the sample upload, the stream name will become the sample name. You cancel a sample upload by issuing pa_stream_disconnect() */ +void pa_stream_finish_upload(pa_stream *s); + +/** Play a sample from the sample cache to the specified device. If the latter is NULL use the default sink. Returns an operation object */ +pa_operation* pa_context_play_sample(pa_context *c, const char *name, const char *dev, uint32_t volume, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); + +/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */ +pa_operation* pa_context_remove_sample(pa_context *c, const char *name, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/polyplib-simple.c b/src/polyp/polyplib-simple.c new file mode 100644 index 00000000..7436f007 --- /dev/null +++ b/src/polyp/polyplib-simple.c @@ -0,0 +1,393 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "polyplib-simple.h" +#include "polyplib.h" +#include "mainloop.h" +#include +#include +#include + +struct pa_simple { + pa_mainloop *mainloop; + pa_context *context; + pa_stream *stream; + pa_stream_direction_t direction; + + int dead; + + void *read_data; + size_t read_index, read_length; + pa_usec_t latency; +}; + +static void read_callback(pa_stream *s, const void*data, size_t length, void *userdata); + +static int check_error(pa_simple *p, int *rerror) { + pa_context_state_t cst; + pa_stream_state_t sst; + assert(p); + + if ((cst = pa_context_get_state(p->context)) == PA_CONTEXT_FAILED) + goto fail; + + assert(cst != PA_CONTEXT_TERMINATED); + + if (p->stream) { + if ((sst = pa_stream_get_state(p->stream)) == PA_STREAM_FAILED) + goto fail; + + assert(sst != PA_STREAM_TERMINATED); + } + + return 0; + +fail: + if (rerror) + *rerror = pa_context_errno(p->context); + + p->dead = 1; + + return -1; +} + +static int iterate(pa_simple *p, int block, int *rerror) { + assert(p && p->context && p->mainloop); + + if (check_error(p, rerror) < 0) + return -1; + + if (!block && !pa_context_is_pending(p->context)) + return 0; + + do { + if (pa_mainloop_iterate(p->mainloop, 1, NULL) < 0) { + if (rerror) + *rerror = PA_ERROR_INTERNAL; + return -1; + } + + if (check_error(p, rerror) < 0) + return -1; + + } while (pa_context_is_pending(p->context)); + + + while (pa_mainloop_deferred_pending(p->mainloop)) { + + if (pa_mainloop_iterate(p->mainloop, 0, NULL) < 0) { + if (rerror) + *rerror = PA_ERROR_INTERNAL; + return -1; + } + + if (check_error(p, rerror) < 0) + return -1; + } + + return 0; +} + +pa_simple* pa_simple_new( + const char *server, + const char *name, + pa_stream_direction_t dir, + const char *dev, + const char *stream_name, + const pa_sample_spec *ss, + const pa_buffer_attr *attr, + int *rerror) { + + pa_simple *p; + int error = PA_ERROR_INTERNAL; + assert(ss && (dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD)); + + p = pa_xmalloc(sizeof(pa_simple)); + p->context = NULL; + p->stream = NULL; + p->mainloop = pa_mainloop_new(); + assert(p->mainloop); + p->dead = 0; + p->direction = dir; + p->read_data = NULL; + p->read_index = p->read_length = 0; + p->latency = 0; + + if (!(p->context = pa_context_new(pa_mainloop_get_api(p->mainloop), name))) + goto fail; + + pa_context_connect(p->context, server, 1, NULL); + + /* Wait until the context is ready */ + while (pa_context_get_state(p->context) != PA_CONTEXT_READY) { + if (iterate(p, 1, &error) < 0) + goto fail; + } + + if (!(p->stream = pa_stream_new(p->context, stream_name, ss, NULL))) + goto fail; + + if (dir == PA_STREAM_PLAYBACK) + pa_stream_connect_playback(p->stream, dev, attr, 0, NULL); + else + pa_stream_connect_record(p->stream, dev, attr, 0); + + /* Wait until the stream is ready */ + while (pa_stream_get_state(p->stream) != PA_STREAM_READY) { + if (iterate(p, 1, &error) < 0) + goto fail; + } + + pa_stream_set_read_callback(p->stream, read_callback, p); + + return p; + +fail: + if (rerror) + *rerror = error; + pa_simple_free(p); + return NULL; +} + +void pa_simple_free(pa_simple *s) { + assert(s); + + pa_xfree(s->read_data); + + if (s->stream) + pa_stream_unref(s->stream); + + if (s->context) + pa_context_unref(s->context); + + if (s->mainloop) + pa_mainloop_free(s->mainloop); + + pa_xfree(s); +} + +int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) { + assert(p && data && p->direction == PA_STREAM_PLAYBACK); + + if (p->dead) { + if (rerror) + *rerror = pa_context_errno(p->context); + + return -1; + } + + while (length > 0) { + size_t l; + + while (!(l = pa_stream_writable_size(p->stream))) + if (iterate(p, 1, rerror) < 0) + return -1; + + if (l > length) + l = length; + + pa_stream_write(p->stream, data, l, NULL, 0); + data = (const uint8_t*) data + l; + length -= l; + } + + /* Make sure that no data is pending for write */ + if (iterate(p, 0, rerror) < 0) + return -1; + + return 0; +} + +static void read_callback(pa_stream *s, const void*data, size_t length, void *userdata) { + pa_simple *p = userdata; + assert(s && data && length && p); + + if (p->read_data) { + pa_log(__FILE__": Buffer overflow, dropping incoming memory blocks.\n"); + pa_xfree(p->read_data); + } + + p->read_data = pa_xmemdup(data, p->read_length = length); + p->read_index = 0; +} + +int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) { + assert(p && data && p->direction == PA_STREAM_RECORD); + + if (p->dead) { + if (rerror) + *rerror = pa_context_errno(p->context); + + return -1; + } + + while (length > 0) { + if (p->read_data) { + size_t l = length; + + if (p->read_length <= l) + l = p->read_length; + + memcpy(data, (uint8_t*) p->read_data+p->read_index, l); + + data = (uint8_t*) data + l; + length -= l; + + p->read_index += l; + p->read_length -= l; + + if (!p->read_length) { + pa_xfree(p->read_data); + p->read_data = NULL; + p->read_index = 0; + } + + if (!length) + return 0; + + assert(!p->read_data); + } + + if (iterate(p, 1, rerror) < 0) + return -1; + } + + return 0; +} + +static void drain_or_flush_complete(pa_stream *s, int success, void *userdata) { + pa_simple *p = userdata; + assert(s && p); + if (!success) + p->dead = 1; +} + +int pa_simple_drain(pa_simple *p, int *rerror) { + pa_operation *o; + assert(p && p->direction == PA_STREAM_PLAYBACK); + + if (p->dead) { + if (rerror) + *rerror = pa_context_errno(p->context); + + return -1; + } + + o = pa_stream_drain(p->stream, drain_or_flush_complete, p); + + while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { + if (iterate(p, 1, rerror) < 0) { + pa_operation_cancel(o); + pa_operation_unref(o); + return -1; + } + } + + pa_operation_unref(o); + + if (p->dead && rerror) + *rerror = pa_context_errno(p->context); + + return p->dead ? -1 : 0; +} + +static void latency_complete(pa_stream *s, const pa_latency_info *l, void *userdata) { + pa_simple *p = userdata; + assert(s && p); + + if (!l) + p->dead = 1; + else { + int negative = 0; + p->latency = pa_stream_get_latency(s, l, &negative); + if (negative) + p->latency = 0; + } +} + +pa_usec_t pa_simple_get_playback_latency(pa_simple *p, int *rerror) { + pa_operation *o; + assert(p && p->direction == PA_STREAM_PLAYBACK); + + if (p->dead) { + if (rerror) + *rerror = pa_context_errno(p->context); + + return (pa_usec_t) -1; + } + + p->latency = 0; + o = pa_stream_get_latency_info(p->stream, latency_complete, p); + + while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { + + if (iterate(p, 1, rerror) < 0) { + pa_operation_cancel(o); + pa_operation_unref(o); + return -1; + } + } + + pa_operation_unref(o); + + if (p->dead && rerror) + *rerror = pa_context_errno(p->context); + + return p->dead ? (pa_usec_t) -1 : p->latency; +} + +int pa_simple_flush(pa_simple *p, int *rerror) { + pa_operation *o; + assert(p && p->direction == PA_STREAM_PLAYBACK); + + if (p->dead) { + if (rerror) + *rerror = pa_context_errno(p->context); + + return -1; + } + + o = pa_stream_flush(p->stream, drain_or_flush_complete, p); + + while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) { + if (iterate(p, 1, rerror) < 0) { + pa_operation_cancel(o); + pa_operation_unref(o); + return -1; + } + } + + pa_operation_unref(o); + + if (p->dead && rerror) + *rerror = pa_context_errno(p->context); + + return p->dead ? -1 : 0; +} diff --git a/src/polyp/polyplib-simple.h b/src/polyp/polyplib-simple.h new file mode 100644 index 00000000..b01f30d5 --- /dev/null +++ b/src/polyp/polyplib-simple.h @@ -0,0 +1,80 @@ +#ifndef foopolyplibsimplehfoo +#define foopolyplibsimplehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "sample.h" +#include "polyplib-def.h" +#include + +/** \file + * A simple but limited synchronous playback and recording + * API. This is synchronouse, simplified wrapper around the standard + * asynchronous API. */ + +/** \example pacat-simple.c + * A simple playback tool using the simple API */ + +/** \example parec-simple.c + * A simple recording tool using the simple API */ + +PA_C_DECL_BEGIN + +/** \pa_simple + * An opaque simple connection object */ +typedef struct pa_simple pa_simple; + +/** Create a new connection to the server */ +pa_simple* pa_simple_new( + const char *server, /**< Server name, or NULL for default */ + const char *name, /**< A descriptive name for this client (application name, ...) */ + pa_stream_direction_t dir, /**< Open this stream for recording or playback? */ + const char *dev, /**< Sink (resp. source) name, or NULL for default */ + const char *stream_name, /**< A descriptive name for this client (application name, song title, ...) */ + const pa_sample_spec *ss, /**< The sample type to use */ + const pa_buffer_attr *attr, /**< Buffering attributes, or NULL for default */ + int *error /**< A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here. */ + ); + +/** Close and free the connection to the server. The connection objects becomes invalid when this is called. */ +void pa_simple_free(pa_simple *s); + +/** Write some data to the server */ +int pa_simple_write(pa_simple *s, const void*data, size_t length, int *error); + +/** Wait until all data already written is played by the daemon */ +int pa_simple_drain(pa_simple *s, int *error); + +/** Read some data from the server */ +int pa_simple_read(pa_simple *s, void*data, size_t length, int *error); + +/** Return the playback latency. \since 0.5 */ +pa_usec_t pa_simple_get_playback_latency(pa_simple *s, int *error); + +/** Flush the playback buffer. \since 0.5 */ +int pa_simple_flush(pa_simple *s, int *error); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/polyplib-stream.c b/src/polyp/polyplib-stream.c new file mode 100644 index 00000000..63c9245b --- /dev/null +++ b/src/polyp/polyplib-stream.c @@ -0,0 +1,807 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "polyplib-internal.h" +#include +#include +#include +#include + +#define LATENCY_IPOL_INTERVAL_USEC (10000L) + +pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) { + pa_stream *s; + assert(c); + assert(ss); + + if (!pa_sample_spec_valid(ss)) + return NULL; + + if (map && !pa_channel_map_valid(map)) + return NULL; + + s = pa_xnew(pa_stream, 1); + s->ref = 1; + s->context = c; + s->mainloop = c->mainloop; + + s->read_callback = NULL; + s->read_userdata = NULL; + s->write_callback = NULL; + s->write_userdata = NULL; + s->state_callback = NULL; + s->state_userdata = NULL; + + s->direction = PA_STREAM_NODIRECTION; + s->name = pa_xstrdup(name); + s->sample_spec = *ss; + + if (map) + s->channel_map = *map; + else + pa_channel_map_init_auto(&s->channel_map, ss->channels); + + s->channel = 0; + s->channel_valid = 0; + s->device_index = PA_INVALID_INDEX; + s->requested_bytes = 0; + s->state = PA_STREAM_DISCONNECTED; + memset(&s->buffer_attr, 0, sizeof(s->buffer_attr)); + + s->mcalign = pa_mcalign_new(pa_frame_size(ss), c->memblock_stat); + + s->counter = 0; + s->previous_time = 0; + s->previous_ipol_time = 0; + + s->corked = 0; + s->interpolate = 0; + + s->ipol_usec = 0; + memset(&s->ipol_timestamp, 0, sizeof(s->ipol_timestamp)); + s->ipol_event = NULL; + s->ipol_requested = 0; + + PA_LLIST_PREPEND(pa_stream, c->streams, s); + + return pa_stream_ref(s); +} + +static void stream_free(pa_stream *s) { + assert(s); + + if (s->ipol_event) { + assert(s->mainloop); + s->mainloop->time_free(s->ipol_event); + } + + pa_mcalign_free(s->mcalign); + + pa_xfree(s->name); + pa_xfree(s); +} + +void pa_stream_unref(pa_stream *s) { + assert(s && s->ref >= 1); + + if (--(s->ref) == 0) + stream_free(s); +} + +pa_stream* pa_stream_ref(pa_stream *s) { + assert(s && s->ref >= 1); + s->ref++; + return s; +} + +pa_stream_state_t pa_stream_get_state(pa_stream *s) { + assert(s && s->ref >= 1); + return s->state; +} + +pa_context* pa_stream_get_context(pa_stream *s) { + assert(s && s->ref >= 1); + return s->context; +} + +uint32_t pa_stream_get_index(pa_stream *s) { + assert(s && s->ref >= 1); + return s->device_index; +} + +void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { + assert(s && s->ref >= 1); + + if (s->state == st) + return; + + pa_stream_ref(s); + + s->state = st; + + if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED) && s->context) { + if (s->channel_valid) + pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL); + + PA_LLIST_REMOVE(pa_stream, s->context->streams, s); + pa_stream_unref(s); + } + + if (s->state_callback) + s->state_callback(s, s->state_userdata); + + pa_stream_unref(s); +} + +void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_context *c = userdata; + pa_stream *s; + uint32_t channel; + assert(pd && (command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED) && t && c); + + pa_context_ref(c); + + if (pa_tagstruct_getu32(t, &channel) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(c, PA_ERROR_PROTOCOL); + goto finish; + } + + if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel))) + goto finish; + + c->error = PA_ERROR_KILLED; + pa_stream_set_state(s, PA_STREAM_FAILED); + +finish: + pa_context_unref(c); +} + +void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_stream *s; + pa_context *c = userdata; + uint32_t bytes, channel; + assert(pd && command == PA_COMMAND_REQUEST && t && c); + + pa_context_ref(c); + + if (pa_tagstruct_getu32(t, &channel) < 0 || + pa_tagstruct_getu32(t, &bytes) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(c, PA_ERROR_PROTOCOL); + goto finish; + } + + if (!(s = pa_dynarray_get(c->playback_streams, channel))) + goto finish; + + if (s->state != PA_STREAM_READY) + goto finish; + + pa_stream_ref(s); + + s->requested_bytes += bytes; + + if (s->requested_bytes && s->write_callback) + s->write_callback(s, s->requested_bytes, s->write_userdata); + + pa_stream_unref(s); + +finish: + pa_context_unref(c); +} + +static void ipol_callback(pa_mainloop_api *m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { + struct timeval tv2; + pa_stream *s = userdata; + + pa_stream_ref(s); + +/* pa_log("requesting new ipol data\n"); */ + + if (s->state == PA_STREAM_READY && !s->ipol_requested) { + pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); + s->ipol_requested = 1; + } + + pa_gettimeofday(&tv2); + pa_timeval_add(&tv2, LATENCY_IPOL_INTERVAL_USEC); + + m->time_restart(e, &tv2); + + pa_stream_unref(s); +} + + +void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_stream *s = userdata; + assert(pd && s && s->state == PA_STREAM_CREATING); + + pa_stream_ref(s); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(s->context, command, t) < 0) + goto finish; + + pa_stream_set_state(s, PA_STREAM_FAILED); + goto finish; + } + + if (pa_tagstruct_getu32(t, &s->channel) < 0 || + ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->device_index) < 0) || + ((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0) || + !pa_tagstruct_eof(t)) { + pa_context_fail(s->context, PA_ERROR_PROTOCOL); + goto finish; + } + + s->channel_valid = 1; + pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s); + pa_stream_set_state(s, PA_STREAM_READY); + + if (s->interpolate) { + struct timeval tv; + pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); + + pa_gettimeofday(&tv); + tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */ + + assert(!s->ipol_event); + s->ipol_event = s->mainloop->time_new(s->mainloop, &tv, &ipol_callback, s); + } + + if (s->requested_bytes && s->ref > 1 && s->write_callback) + s->write_callback(s, s->requested_bytes, s->write_userdata); + +finish: + pa_stream_unref(s); +} + +static void create_stream(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags, const pa_cvolume *volume) { + pa_tagstruct *t; + uint32_t tag; + assert(s && s->ref >= 1 && s->state == PA_STREAM_DISCONNECTED); + + pa_stream_ref(s); + + s->interpolate = !!(flags & PA_STREAM_INTERPOLATE_LATENCY); + pa_stream_trash_ipol(s); + + if (attr) + s->buffer_attr = *attr; + else { + /* half a second */ + s->buffer_attr.tlength = pa_bytes_per_second(&s->sample_spec)/2; + s->buffer_attr.maxlength = (s->buffer_attr.tlength*3)/2; + s->buffer_attr.minreq = s->buffer_attr.tlength/100; + s->buffer_attr.prebuf = s->buffer_attr.tlength - s->buffer_attr.minreq; + s->buffer_attr.fragsize = s->buffer_attr.tlength/100; + } + + pa_stream_set_state(s, PA_STREAM_CREATING); + + t = pa_tagstruct_new(NULL, 0); + assert(t); + + if (!dev) { + if (s->direction == PA_STREAM_PLAYBACK) + dev = s->context->conf->default_sink; + else + dev = s->context->conf->default_source; + } + + pa_tagstruct_put(t, + PA_TAG_U32, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM, + PA_TAG_U32, tag = s->context->ctag++, + PA_TAG_STRING, s->name, + PA_TAG_SAMPLE_SPEC, &s->sample_spec, + PA_TAG_CHANNEL_MAP, &s->channel_map, + PA_TAG_U32, PA_INVALID_INDEX, + PA_TAG_STRING, dev, + PA_TAG_U32, s->buffer_attr.maxlength, + PA_TAG_BOOLEAN, !!(flags & PA_STREAM_START_CORKED), + PA_TAG_INVALID); + + if (s->direction == PA_STREAM_PLAYBACK) { + pa_cvolume cv; + pa_tagstruct_put(t, + PA_TAG_U32, s->buffer_attr.tlength, + PA_TAG_U32, s->buffer_attr.prebuf, + PA_TAG_U32, s->buffer_attr.minreq, + PA_TAG_INVALID); + + if (!volume) { + pa_cvolume_reset(&cv, s->sample_spec.channels); + volume = &cv; + } + + pa_tagstruct_put_cvolume(t, volume); + } else + pa_tagstruct_putu32(t, s->buffer_attr.fragsize); + + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s); + + pa_stream_unref(s); +} + +void pa_stream_connect_playback(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags, pa_cvolume *volume) { + assert(s && s->context->state == PA_CONTEXT_READY && s->ref >= 1); + s->direction = PA_STREAM_PLAYBACK; + create_stream(s, dev, attr, flags, volume); +} + +void pa_stream_connect_record(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags) { + assert(s && s->context->state == PA_CONTEXT_READY && s->ref >= 1); + s->direction = PA_STREAM_RECORD; + create_stream(s, dev, attr, flags, 0); +} + +void pa_stream_write(pa_stream *s, const void *data, size_t length, void (*free_cb)(void *p), size_t delta) { + pa_memchunk chunk; + assert(s && s->context && data && length && s->state == PA_STREAM_READY && s->ref >= 1); + + if (free_cb) { + chunk.memblock = pa_memblock_new_user((void*) data, length, free_cb, 1, s->context->memblock_stat); + assert(chunk.memblock && chunk.memblock->data); + } else { + chunk.memblock = pa_memblock_new(length, s->context->memblock_stat); + assert(chunk.memblock && chunk.memblock->data); + memcpy(chunk.memblock->data, data, length); + } + chunk.index = 0; + chunk.length = length; + + pa_pstream_send_memblock(s->context->pstream, s->channel, delta, &chunk); + pa_memblock_unref(chunk.memblock); + + if (length < s->requested_bytes) + s->requested_bytes -= length; + else + s->requested_bytes = 0; + + s->counter += length; +} + +size_t pa_stream_writable_size(pa_stream *s) { + assert(s && s->ref >= 1); + return s->state == PA_STREAM_READY ? s->requested_bytes : 0; +} + +pa_operation * pa_stream_drain(pa_stream *s, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(s && s->ref >= 1 && s->state == PA_STREAM_READY); + + o = pa_operation_new(s->context, s); + assert(o); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_DRAIN_PLAYBACK_STREAM); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + pa_tagstruct_putu32(t, s->channel); + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o); + + return pa_operation_ref(o); +} + +static void stream_get_latency_info_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + pa_latency_info i, *p = NULL; + struct timeval local, remote, now; + assert(pd && o && o->stream && o->context); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + } else if (pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 || + pa_tagstruct_get_usec(t, &i.sink_usec) < 0 || + pa_tagstruct_get_usec(t, &i.source_usec) < 0 || + pa_tagstruct_get_boolean(t, &i.playing) < 0 || + pa_tagstruct_getu32(t, &i.queue_length) < 0 || + pa_tagstruct_get_timeval(t, &local) < 0 || + pa_tagstruct_get_timeval(t, &remote) < 0 || + pa_tagstruct_getu64(t, &i.counter) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } else { + pa_gettimeofday(&now); + + if (pa_timeval_cmp(&local, &remote) <= 0 && pa_timeval_cmp(&remote, &now) <= 0) { + /* local and remote seem to have synchronized clocks */ + + if (o->stream->direction == PA_STREAM_PLAYBACK) + i.transport_usec = pa_timeval_diff(&remote, &local); + else + i.transport_usec = pa_timeval_diff(&now, &remote); + + i.synchronized_clocks = 1; + i.timestamp = remote; + } else { + /* clocks are not synchronized, let's estimate latency then */ + i.transport_usec = pa_timeval_diff(&now, &local)/2; + i.synchronized_clocks = 0; + i.timestamp = local; + pa_timeval_add(&i.timestamp, i.transport_usec); + } + + if (o->stream->interpolate) { +/* pa_log("new interpol data\n"); */ + o->stream->ipol_timestamp = i.timestamp; + o->stream->ipol_usec = pa_stream_get_time(o->stream, &i); + o->stream->ipol_requested = 0; + } + + p = &i; + } + + if (o->callback) { + void (*cb)(pa_stream *s, const pa_latency_info *_i, void *_userdata) = (void (*)(pa_stream *s, const pa_latency_info *_i, void *_userdata)) o->callback; + cb(o->stream, p, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_stream_get_latency_info(pa_stream *s, void (*cb)(pa_stream *p, const pa_latency_info*i, void *userdata), void *userdata) { + uint32_t tag; + pa_operation *o; + pa_tagstruct *t; + struct timeval now; + assert(s && s->direction != PA_STREAM_UPLOAD); + + o = pa_operation_new(s->context, s); + assert(o); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + pa_tagstruct_putu32(t, s->channel); + + pa_gettimeofday(&now); + pa_tagstruct_put_timeval(t, &now); + pa_tagstruct_putu64(t, s->counter); + + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_info_callback, o); + + return pa_operation_ref(o); +} + +void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_stream *s = userdata; + assert(pd && s && s->ref >= 1); + + pa_stream_ref(s); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(s->context, command, t) < 0) + goto finish; + + pa_stream_set_state(s, PA_STREAM_FAILED); + goto finish; + } else if (!pa_tagstruct_eof(t)) { + pa_context_fail(s->context, PA_ERROR_PROTOCOL); + goto finish; + } + + pa_stream_set_state(s, PA_STREAM_TERMINATED); + +finish: + pa_stream_unref(s); +} + +void pa_stream_disconnect(pa_stream *s) { + pa_tagstruct *t; + uint32_t tag; + assert(s && s->ref >= 1); + + if (!s->channel_valid || !s->context->state == PA_CONTEXT_READY) + return; + + pa_stream_ref(s); + + t = pa_tagstruct_new(NULL, 0); + assert(t); + + pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_DELETE_PLAYBACK_STREAM : + (s->direction == PA_STREAM_RECORD ? PA_COMMAND_DELETE_RECORD_STREAM : PA_COMMAND_DELETE_UPLOAD_STREAM)); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + pa_tagstruct_putu32(t, s->channel); + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_disconnect_callback, s); + + pa_stream_unref(s); +} + +void pa_stream_set_read_callback(pa_stream *s, void (*cb)(pa_stream *p, const void*data, size_t length, void *userdata), void *userdata) { + assert(s && s->ref >= 1); + s->read_callback = cb; + s->read_userdata = userdata; +} + +void pa_stream_set_write_callback(pa_stream *s, void (*cb)(pa_stream *p, size_t length, void *userdata), void *userdata) { + assert(s && s->ref >= 1); + s->write_callback = cb; + s->write_userdata = userdata; +} + +void pa_stream_set_state_callback(pa_stream *s, void (*cb)(pa_stream *s, void *userdata), void *userdata) { + assert(s && s->ref >= 1); + s->state_callback = cb; + s->state_userdata = userdata; +} + +void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int success = 1; + assert(pd && o && o->context && o->ref >= 1); + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t) < 0) + goto finish; + + success = 0; + } else if (!pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERROR_PROTOCOL); + goto finish; + } + + if (o->callback) { + void (*cb)(pa_stream *s, int _success, void *_userdata) = (void (*)(pa_stream *s, int _success, void *_userdata)) o->callback; + cb(o->stream, success, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation* pa_stream_cork(pa_stream *s, int b, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(s && s->ref >= 1 && s->state == PA_STREAM_READY); + + if (s->interpolate) { + if (!s->corked && b) + /* Pausing */ + s->ipol_usec = pa_stream_get_interpolated_time(s); + else if (s->corked && !b) + /* Unpausing */ + pa_gettimeofday(&s->ipol_timestamp); + } + + s->corked = b; + + o = pa_operation_new(s->context, s); + assert(o); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CORK_PLAYBACK_STREAM : PA_COMMAND_CORK_RECORD_STREAM); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + pa_tagstruct_putu32(t, s->channel); + pa_tagstruct_put_boolean(t, !!b); + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o); + + pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); + + return pa_operation_ref(o); +} + +static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) { + pa_tagstruct *t; + pa_operation *o; + uint32_t tag; + assert(s && s->ref >= 1 && s->state == PA_STREAM_READY); + + o = pa_operation_new(s->context, s); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, command); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + pa_tagstruct_putu32(t, s->channel); + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o); + + return pa_operation_ref(o); +} + +pa_operation* pa_stream_flush(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) { + pa_operation *o; + o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata); + pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); + return o; +} + +pa_operation* pa_stream_prebuf(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) { + pa_operation *o; + o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata); + pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); + return o; +} + +pa_operation* pa_stream_trigger(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata) { + pa_operation *o; + o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata); + pa_operation_unref(pa_stream_get_latency_info(s, NULL, NULL)); + return o; +} + +pa_operation* pa_stream_set_name(pa_stream *s, const char *name, void(*cb)(pa_stream*c, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(s && s->ref >= 1 && s->state == PA_STREAM_READY && name && s->direction != PA_STREAM_UPLOAD); + + o = pa_operation_new(s->context, s); + assert(o); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + pa_tagstruct_putu32(t, s->channel); + pa_tagstruct_puts(t, name); + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o); + + return pa_operation_ref(o); +} + +uint64_t pa_stream_get_counter(pa_stream *s) { + assert(s); + return s->counter; +} + +pa_usec_t pa_stream_get_time(pa_stream *s, const pa_latency_info *i) { + pa_usec_t usec; + assert(s); + + usec = pa_bytes_to_usec(i->counter, &s->sample_spec); + + if (i) { + if (s->direction == PA_STREAM_PLAYBACK) { + pa_usec_t latency = i->transport_usec + i->buffer_usec + i->sink_usec; + if (usec < latency) + usec = 0; + else + usec -= latency; + + } else if (s->direction == PA_STREAM_RECORD) { + usec += i->source_usec + i->buffer_usec + i->transport_usec; + + if (usec > i->sink_usec) + usec -= i->sink_usec; + else + usec = 0; + } + } + + if (usec < s->previous_time) + usec = s->previous_time; + + s->previous_time = usec; + + return usec; +} + +static pa_usec_t time_counter_diff(pa_stream *s, pa_usec_t t, pa_usec_t c, int *negative) { + assert(s); + + if (negative) + *negative = 0; + + if (c < t) { + if (s->direction == PA_STREAM_RECORD) { + if (negative) + *negative = 1; + + return t-c; + } else + return 0; + } else + return c-t; +} + +pa_usec_t pa_stream_get_latency(pa_stream *s, const pa_latency_info *i, int *negative) { + pa_usec_t t, c; + assert(s && i); + + t = pa_stream_get_time(s, i); + c = pa_bytes_to_usec(s->counter, &s->sample_spec); + + return time_counter_diff(s, t, c, negative); +} + +const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) { + assert(s); + return &s->sample_spec; +} + +void pa_stream_trash_ipol(pa_stream *s) { + assert(s); + + if (!s->interpolate) + return; + + memset(&s->ipol_timestamp, 0, sizeof(s->ipol_timestamp)); + s->ipol_usec = 0; +} + +pa_usec_t pa_stream_get_interpolated_time(pa_stream *s) { + pa_usec_t usec; + assert(s && s->interpolate); + + if (s->corked) + usec = s->ipol_usec; + else { + if (s->ipol_timestamp.tv_sec == 0) + usec = 0; + else + usec = s->ipol_usec + pa_timeval_age(&s->ipol_timestamp); + } + + if (usec < s->previous_ipol_time) + usec = s->previous_ipol_time; + + s->previous_ipol_time = usec; + + return usec; +} + +pa_usec_t pa_stream_get_interpolated_latency(pa_stream *s, int *negative) { + pa_usec_t t, c; + assert(s && s->interpolate); + + t = pa_stream_get_interpolated_time(s); + c = pa_bytes_to_usec(s->counter, &s->sample_spec); + return time_counter_diff(s, t, c, negative); +} diff --git a/src/polyp/polyplib-stream.h b/src/polyp/polyplib-stream.h new file mode 100644 index 00000000..bc828b71 --- /dev/null +++ b/src/polyp/polyplib-stream.h @@ -0,0 +1,181 @@ +#ifndef foopolyplibstreamhfoo +#define foopolyplibstreamhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include +#include +#include +#include +#include + +/** \file + * Audio streams for input, output and sample upload */ + +PA_C_DECL_BEGIN + +/** \pa_stream + * An opaque stream for playback or recording */ +typedef struct pa_stream pa_stream; + +/** Create a new, unconnected stream with the specified name and sample type */ +pa_stream* pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map); + +/** Decrease the reference counter by one */ +void pa_stream_unref(pa_stream *s); + +/** Increase the reference counter by one */ +pa_stream *pa_stream_ref(pa_stream *s); + +/** Return the current state of the stream */ +pa_stream_state_t pa_stream_get_state(pa_stream *p); + +/** Return the context this stream is attached to */ +pa_context* pa_stream_get_context(pa_stream *p); + +/** Return the device (sink input or source output) index this stream is connected to */ +uint32_t pa_stream_get_index(pa_stream *s); + +/** Connect the stream to a sink */ +void pa_stream_connect_playback( + pa_stream *s, + const char *dev, + const pa_buffer_attr *attr, + pa_stream_flags_t flags, + pa_cvolume *volume); + +/** Connect the stream to a source */ +void pa_stream_connect_record( + pa_stream *s, + const char *dev, + const pa_buffer_attr *attr, + pa_stream_flags_t flags); + +/** Disconnect a stream from a source/sink */ +void pa_stream_disconnect(pa_stream *s); + +/** Write some data to the server (for playback sinks), if free_cb is + * non-NULL this routine is called when all data has been written out + * and an internal reference to the specified data is kept, the data + * is not copied. If NULL, the data is copied into an internal + * buffer. */ +void pa_stream_write(pa_stream *p /**< The stream to use */, + const void *data /**< The data to write */, + size_t length /**< The length of the data to write */, + void (*free_cb)(void *p) /**< A cleanup routine for the data or NULL to request an internal copy */, + size_t delta /**< Drop this many + bytes in the playback + buffer before writing + this data. Use + (size_t) -1 for + clearing the whole + playback + buffer. Normally you + will specify 0 here, + i.e. append to the + playback buffer. If + the value given here + is greater than the + buffered data length + the buffer is cleared + and the data is + written to the + buffer's start. This + value is ignored on + upload streams. */); + +/** Return the amount of bytes that may be written using pa_stream_write() */ +size_t pa_stream_writable_size(pa_stream *p); + +/** Drain a playback stream */ +pa_operation* pa_stream_drain(pa_stream *s, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata); + +/** Get the playback latency of a stream */ +pa_operation* pa_stream_get_latency_info(pa_stream *p, void (*cb)(pa_stream *p, const pa_latency_info *i, void *userdata), void *userdata); + +/** Set the callback function that is called whenever the state of the stream changes */ +void pa_stream_set_state_callback(pa_stream *s, void (*cb)(pa_stream *s, void *userdata), void *userdata); + +/** Set the callback function that is called when new data may be + * written to the stream. */ +void pa_stream_set_write_callback(pa_stream *p, void (*cb)(pa_stream *p, size_t length, void *userdata), void *userdata); + +/** Set the callback function that is called when new data is available from the stream */ +void pa_stream_set_read_callback(pa_stream *p, void (*cb)(pa_stream *p, const void*data, size_t length, void *userdata), void *userdata); + +/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. \since 0.3 */ +pa_operation* pa_stream_cork(pa_stream *s, int b, void (*cb) (pa_stream*s, int success, void *userdata), void *userdata); + +/** Flush the playback buffer of this stream. Most of the time you're + * better off using the parameter delta of pa_stream_write() instead of this + * function. Available on both playback and recording streams. \since 0.3 */ +pa_operation* pa_stream_flush(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata); + +/** Reenable prebuffering. Available for playback streams only. \since 0.6 */ +pa_operation* pa_stream_prebuf(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata); + +/** Request immediate start of playback on this stream. This disables + * prebuffering as specified in the pa_buffer_attr structure. Available for playback streams only. \since + * 0.3 */ +pa_operation* pa_stream_trigger(pa_stream *s, void (*cb)(pa_stream *s, int success, void *userdata), void *userdata); + +/** Rename the stream. \since 0.5 */ +pa_operation* pa_stream_set_name(pa_stream *s, const char *name, void(*cb)(pa_stream*c, int success, void *userdata), void *userdata); + +/** Return the total number of bytes written to/read from the + * stream. This counter is not reset on pa_stream_flush(), you may do + * this yourself using pa_stream_reset_counter(). \since 0.6 */ +uint64_t pa_stream_get_counter(pa_stream *s); + +/** Return the current playback/recording time. This is based on the + * counter accessible with pa_stream_get_counter(). This function + * requires a pa_latency_info structure as argument, which should be + * acquired using pa_stream_get_latency(). \since 0.6 */ +pa_usec_t pa_stream_get_time(pa_stream *s, const pa_latency_info *i); + +/** Return the total stream latency. Thus function requires a + * pa_latency_info structure as argument, which should be aquired + * using pa_stream_get_latency(). In case the stream is a monitoring + * stream the result can be negative, i.e. the captured samples are + * not yet played. In this case *negative is set to 1. \since 0.6 */ +pa_usec_t pa_stream_get_latency(pa_stream *s, const pa_latency_info *i, int *negative); + +/** Return the interpolated playback/recording time. Requires the + * PA_STREAM_INTERPOLATE_LATENCY bit set when creating the stream. In + * contrast to pa_stream_get_latency() this function doesn't require + * a whole roundtrip for response. \since 0.6 */ +pa_usec_t pa_stream_get_interpolated_time(pa_stream *s); + +/** Return the interpolated playback/recording latency. Requires the + * PA_STREAM_INTERPOLATE_LATENCY bit set when creating the + * stream. \since 0.6 */ +pa_usec_t pa_stream_get_interpolated_latency(pa_stream *s, int *negative); + +/** Return a pointer to the streams sample specification. \since 0.6 */ +const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/polyplib-subscribe.c b/src/polyp/polyplib-subscribe.c new file mode 100644 index 00000000..13fcfb42 --- /dev/null +++ b/src/polyp/polyplib-subscribe.c @@ -0,0 +1,81 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "polyplib-subscribe.h" +#include "polyplib-internal.h" +#include +#include + +void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_context *c = userdata; + pa_subscription_event_type_t e; + uint32_t index; + assert(pd && command == PA_COMMAND_SUBSCRIBE_EVENT && t && c); + + pa_context_ref(c); + + if (pa_tagstruct_getu32(t, &e) < 0 || + pa_tagstruct_getu32(t, &index) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(c, PA_ERROR_PROTOCOL); + goto finish; + } + + if (c->subscribe_callback) + c->subscribe_callback(c, e, index, c->subscribe_userdata); + +finish: + pa_context_unref(c); +} + + +pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, void (*cb)(pa_context *c, int success, void *userdata), void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + assert(c); + + o = pa_operation_new(c, NULL); + o->callback = (pa_operation_callback) cb; + o->userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_putu32(t, m); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, o); + + return pa_operation_ref(o); +} + +void pa_context_set_subscribe_callback(pa_context *c, void (*cb)(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata) { + assert(c); + c->subscribe_callback = cb; + c->subscribe_userdata = userdata; +} diff --git a/src/polyp/polyplib-subscribe.h b/src/polyp/polyplib-subscribe.h new file mode 100644 index 00000000..920c9853 --- /dev/null +++ b/src/polyp/polyplib-subscribe.h @@ -0,0 +1,47 @@ +#ifndef foopolyplibsubscribehfoo +#define foopolyplibsubscribehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include +#include + +/** \file + * Daemon introspection event subscription subsystem. Use this + * to be notified whenever the internal layout of daemon changes: + * i.e. entities such as sinks or sources are create, removed or + * modified. */ + +PA_C_DECL_BEGIN + +/** Enable event notification */ +pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, void (*cb)(pa_context *c, int success, void *userdata), void *userdata); + +/** Set the context specific call back function that is called whenever the state of the daemon changes */ +void pa_context_set_subscribe_callback(pa_context *c, void (*cb)(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/polyplib-version.h.in b/src/polyp/polyplib-version.h.in new file mode 100644 index 00000000..89e0a0e5 --- /dev/null +++ b/src/polyp/polyplib-version.h.in @@ -0,0 +1,47 @@ +#ifndef foopolyplibversionhfoo /*-*-C-*-*/ +#define foopolyplibversionhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* WARNING: Make sure to edit the real source file polyplib-version.h.in! */ + +/** \file + * Define header version */ + +PA_C_DECL_BEGIN + +/** Return the version of the header files. Keep in mind that this is +a macro and not a function, so it is impossible to get the pointer of +it. */ +#define pa_get_headers_version() ("@PACKAGE_VERSION@") + +/** Return the version of the library the current application is linked to. */ +const char* pa_get_library_version(void); + +/** The current API version. Version 6 relates to polypaudio + * 0.6. Prior versions (i.e. Polypaudio 0.5.1 and older) have + * PA_API_VERSION undefined. */ +#define PA_API_VERSION @PA_API_VERSION@ + +PA_C_DECL_END + +#endif diff --git a/src/polyp/polyplib.h b/src/polyp/polyplib.h new file mode 100644 index 00000000..b9b9b447 --- /dev/null +++ b/src/polyp/polyplib.h @@ -0,0 +1,86 @@ +#ifndef foopolyplibhfoo +#define foopolyplibhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** \file + * Include all polyplib header file at once. The following files are included: \ref mainloop-api.h, \ref sample.h, + * \ref polyplib-def.h, \ref polyplib-context.h, \ref polyplib-stream.h, + * \ref polyplib-introspect.h, \ref polyplib-subscribe.h and \ref polyplib-scache.h \ref polyplib-version.h + * at once */ + +/** \mainpage + * + * \section intro_sec Introduction + * + * This document describes the client API for the polypaudio sound + * server. The API comes in two flavours: + * + * \li The complete but somewhat complicated to use asynchronous API + * \li And the simplified, easy to use, but limited synchronous API + * + * The polypaudio client libraries are thread safe as long as all + * objects created by any library function are accessed from the thread + * that created them only. + * + * \section simple_sec Simple API + * + * Use this if you develop your program in synchronous style and just + * need a way to play or record data on the sound server. See + * \ref polyplib-simple.h for more details. + * + * \section async_api Asynchronous API + * + * Use this if you develop your programs in asynchronous, main loop + * based style or want to use advanced features of the polypaudio + * API. A good starting point is \ref polyplib-context.h + * + * The asynchronous API relies on an abstract main loop API that is + * described in \ref mainloop-api.h. Two distinct implementations are + * available: + * + * \li \ref mainloop.h: a minimal but fast implementation based on poll() + * \li \ref glib-mainloop.h: a wrapper around GLIB's main loop + * + * UNIX signals may be hooked to a main loop using the functions from + * \ref mainloop-signal.h + * + * \section pkgconfig pkg-config + * + * The polypaudio libraries provide pkg-config snippets for the different modules. To use the + * asynchronous API use "polyplib" as pkg-config file. GLIB main loop + * support is available as "polyplib-glib-mainloop". The simple + * synchronous API is available as "polyplib-simple". + */ + +#endif diff --git a/src/polyp/sample.c b/src/polyp/sample.c new file mode 100644 index 00000000..d587170c --- /dev/null +++ b/src/polyp/sample.c @@ -0,0 +1,149 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "sample.h" + +size_t pa_sample_size(const pa_sample_spec *spec) { + assert(spec); + + switch (spec->format) { + case PA_SAMPLE_U8: + case PA_SAMPLE_ULAW: + case PA_SAMPLE_ALAW: + return 1; + case PA_SAMPLE_S16LE: + case PA_SAMPLE_S16BE: + return 2; + case PA_SAMPLE_FLOAT32LE: + case PA_SAMPLE_FLOAT32BE: + return 4; + default: + assert(0); + } +} + +size_t pa_frame_size(const pa_sample_spec *spec) { + assert(spec); + + return pa_sample_size(spec) * spec->channels; +} + +size_t pa_bytes_per_second(const pa_sample_spec *spec) { + assert(spec); + return spec->rate*pa_frame_size(spec); +} + +pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) { + assert(spec); + + return (pa_usec_t) (((double) length/pa_frame_size(spec)*1000000)/spec->rate); +} + +int pa_sample_spec_valid(const pa_sample_spec *spec) { + assert(spec); + + if (spec->rate <= 0 || + spec->channels <= 0 || + spec->channels > PA_CHANNELS_MAX || + spec->format >= PA_SAMPLE_MAX || + spec->format < 0) + return 0; + + return 1; +} + +int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) { + assert(a && b); + + return (a->format == b->format) && (a->rate == b->rate) && (a->channels == b->channels); +} + +const char *pa_sample_format_to_string(pa_sample_format_t f) { + static const char* const table[]= { + [PA_SAMPLE_U8] = "u8", + [PA_SAMPLE_ALAW] = "aLaw", + [PA_SAMPLE_ULAW] = "uLaw", + [PA_SAMPLE_S16LE] = "s16le", + [PA_SAMPLE_S16BE] = "s16be", + [PA_SAMPLE_FLOAT32LE] = "float32le", + [PA_SAMPLE_FLOAT32BE] = "float32be", + }; + + if (f >= PA_SAMPLE_MAX) + return NULL; + + return table[f]; +} + +char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) { + assert(s && l && spec); + + if (!pa_sample_spec_valid(spec)) + snprintf(s, l, "Invalid"); + else + snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate); + + return s; +} + +void pa_bytes_snprint(char *s, size_t l, unsigned v) { + if (v >= ((unsigned) 1024)*1024*1024) + snprintf(s, l, "%0.1f GB", ((double) v)/1024/1024/1024); + else if (v >= ((unsigned) 1024)*1024) + snprintf(s, l, "%0.1f MB", ((double) v)/1024/1024); + else if (v >= (unsigned) 1024) + snprintf(s, l, "%0.1f KB", ((double) v)/1024); + else + snprintf(s, l, "%u B", (unsigned) v); +} + +pa_sample_format_t pa_parse_sample_format(const char *format) { + + if (strcasecmp(format, "s16le") == 0) + return PA_SAMPLE_S16LE; + else if (strcasecmp(format, "s16be") == 0) + return PA_SAMPLE_S16BE; + else if (strcasecmp(format, "s16ne") == 0 || strcasecmp(format, "s16") == 0 || strcasecmp(format, "16") == 0) + return PA_SAMPLE_S16NE; + else if (strcasecmp(format, "u8") == 0 || strcasecmp(format, "8") == 0) + return PA_SAMPLE_U8; + else if (strcasecmp(format, "float32") == 0 || strcasecmp(format, "float32ne") == 0) + return PA_SAMPLE_FLOAT32; + else if (strcasecmp(format, "float32le") == 0) + return PA_SAMPLE_FLOAT32LE; + else if (strcasecmp(format, "float32be") == 0) + return PA_SAMPLE_FLOAT32BE; + else if (strcasecmp(format, "ulaw") == 0) + return PA_SAMPLE_ULAW; + else if (strcasecmp(format, "alaw") == 0) + return PA_SAMPLE_ALAW; + + return -1; +} diff --git a/src/polyp/sample.h b/src/polyp/sample.h new file mode 100644 index 00000000..c1b98f1c --- /dev/null +++ b/src/polyp/sample.h @@ -0,0 +1,120 @@ +#ifndef foosamplehfoo +#define foosamplehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include + +#include + +/** \file + * Constants and routines for sample type handling */ + +PA_C_DECL_BEGIN + +/* Maximum allowed channels */ +#define PA_CHANNELS_MAX 16 + +/** Sample format */ +typedef enum pa_sample_format { + PA_SAMPLE_U8, /**< Unsigned 8 Bit PCM */ + PA_SAMPLE_ALAW, /**< 8 Bit a-Law */ + PA_SAMPLE_ULAW, /**< 8 Bit mu-Law */ + PA_SAMPLE_S16LE, /**< Signed 16 Bit PCM, little endian (PC) */ + PA_SAMPLE_S16BE, /**< Signed 16 Bit PCM, big endian */ + PA_SAMPLE_FLOAT32LE, /**< 32 Bit IEEE floating point, little endian, range -1..1 */ + PA_SAMPLE_FLOAT32BE, /**< 32 Bit IEEE floating point, big endian, range -1..1 */ + PA_SAMPLE_MAX, /**< Upper limit of valid sample types */ + PA_SAMPLE_INVALID = -1 /**< An invalid value */ +} pa_sample_format_t; + +#ifdef WORDS_BIGENDIAN +/** Signed 16 Bit PCM, native endian */ +#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE +/** 32 Bit IEEE floating point, native endian */ +#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE +/** Signed 16 Bit PCM reverse endian */ +#define PA_SAMPLE_S16RE PA_SAMPLE_S16LE +/** 32 Bit IEEE floating point, reverse endian */ +#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32LE +#else +/** Signed 16 Bit PCM, native endian */ +#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE +/** 32 Bit IEEE floating point, native endian */ +#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE +/** Signed 16 Bit PCM reverse endian */ +#define PA_SAMPLE_S16RE PA_SAMPLE_S16BE +/** 32 Bit IEEE floating point, reverse endian */ +#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32BE +#endif + +/** A Shortcut for PA_SAMPLE_FLOAT32NE */ +#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32NE + +/** A sample format and attribute specification */ +typedef struct pa_sample_spec { + pa_sample_format_t format; /**< The sample format */ + uint32_t rate; /**< The sample rate. (e.g. 44100) */ + uint8_t channels; /**< Audio channels. (1 for mono, 2 for stereo, ...) */ +} pa_sample_spec; + +/** Type for usec specifications (unsigned). May be either 32 or 64 bit, depending on the architecture */ +typedef uint64_t pa_usec_t; + +/** Return the amount of bytes playback of a second of audio with the specified sample type takes */ +size_t pa_bytes_per_second(const pa_sample_spec *spec); + +/** Return the size of a frame with the specific sample type */ +size_t pa_frame_size(const pa_sample_spec *spec); + +/** Return the size of a sample with the specific sample type */ +size_t pa_sample_size(const pa_sample_spec *spec); + +/** Calculate the time the specified bytes take to play with the specified sample type */ +pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec); + +/** Return non-zero when the sample type specification is valid */ +int pa_sample_spec_valid(const pa_sample_spec *spec); + +/** Return non-zero when the two sample type specifications match */ +int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b); + +/* Return a descriptive string for the specified sample format. \since 0.8 */ +const char *pa_sample_format_to_string(pa_sample_format_t f); + +/** Parse a sample format text. Inverse of pa_sample_format_to_string() */ +pa_sample_format_t pa_parse_sample_format(const char *format); + +/** Maximum required string length for pa_sample_spec_snprint() */ +#define PA_SAMPLE_SPEC_SNPRINT_MAX 32 + +/** Pretty print a sample type specification to a string */ +char* pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec); + +/** Pretty print a byte size value. (i.e. "2.5 MB") */ +void pa_bytes_snprint(char *s, size_t l, unsigned v); + +PA_C_DECL_END + +#endif diff --git a/src/polyp/volume.c b/src/polyp/volume.c new file mode 100644 index 00000000..0f153141 --- /dev/null +++ b/src/polyp/volume.c @@ -0,0 +1,176 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "volume.h" + +int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) { + int i; + assert(a); + assert(b); + + if (a->channels != b->channels) + return 0; + + for (i = 0; i < a->channels; i++) + if (a->values[i] != b->values[i]) + return 0; + + return 1; +} + +pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) { + int i; + + assert(a); + assert(channels > 0); + assert(channels <= PA_CHANNELS_MAX); + + a->channels = channels; + + for (i = 0; i < a->channels; i++) + a->values[i] = v; + + return a; +} + +pa_volume_t pa_cvolume_avg(const pa_cvolume *a) { + uint64_t sum = 0; + int i; + assert(a); + + for (i = 0; i < a->channels; i++) + sum += a->values[i]; + + sum /= a->channels; + + return (pa_volume_t) sum; +} + +pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) { + return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a)* pa_sw_volume_to_linear(b)); +} + +#define USER_DECIBEL_RANGE 30 + +pa_volume_t pa_sw_volume_from_dB(double dB) { + if (dB <= -USER_DECIBEL_RANGE) + return PA_VOLUME_MUTED; + + return (pa_volume_t) ((dB/USER_DECIBEL_RANGE+1)*PA_VOLUME_NORM); +} + +double pa_sw_volume_to_dB(pa_volume_t v) { + if (v == PA_VOLUME_MUTED) + return PA_DECIBEL_MININFTY; + + return ((double) v/PA_VOLUME_NORM-1)*USER_DECIBEL_RANGE; +} + +pa_volume_t pa_sw_volume_from_linear(double v) { + + if (v <= 0) + return PA_VOLUME_MUTED; + + if (v > .999 && v < 1.001) + return PA_VOLUME_NORM; + + return pa_sw_volume_from_dB(20*log10(v)); +} + +double pa_sw_volume_to_linear(pa_volume_t v) { + + if (v == PA_VOLUME_MUTED) + return 0; + + return pow(10, pa_sw_volume_to_dB(v)/20); +} + +char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) { + unsigned channel; + int first = 1; + char *e; + + assert(s); + assert(l > 0); + assert(c); + + *(e = s) = 0; + + for (channel = 0; channel < c->channels && l > 1; channel++) { + l -= snprintf(e, l, "%s%u: %3u%%", + first ? "" : " ", + channel, + (c->values[channel]*100)/PA_VOLUME_NORM); + + e = strchr(e, 0); + first = 0; + } + + return s; +} + +/** Return non-zero if the volume of all channels is equal to the specified value */ +int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) { + unsigned c; + assert(a); + + for (c = 0; c < a->channels; c++) + if (a->values[c] != v) + return 0; + + return 1; +} + +pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { + unsigned i; + + assert(dest); + assert(a); + assert(b); + + for (i = 0; i < a->channels || i < b->channels || i < PA_CHANNELS_MAX; i++) { + + dest->values[i] = pa_sw_volume_multiply( + i < a->channels ? a->values[i] : PA_VOLUME_NORM, + i < b->channels ? b->values[i] : PA_VOLUME_NORM); + } + + dest->channels = i; + + return dest; +} + +int pa_cvolume_valid(const pa_cvolume *v) { + assert(v); + + if (v->channels <= 0 || v->channels > PA_CHANNELS_MAX) + return 0; + + return 1; +} diff --git a/src/polyp/volume.h b/src/polyp/volume.h new file mode 100644 index 00000000..b2a48084 --- /dev/null +++ b/src/polyp/volume.h @@ -0,0 +1,107 @@ +#ifndef foovolumehfoo +#define foovolumehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include + +/** \file + * Constants and routines for volume handling */ + +PA_C_DECL_BEGIN + +/** Volume specification: + * PA_VOLUME_MUTED: silence; + * < PA_VOLUME_NORM: decreased volume; + * PA_VOLUME_NORM: normal volume; + * > PA_VOLUME_NORM: increased volume */ +typedef uint32_t pa_volume_t; + +/** Normal volume (100%) */ +#define PA_VOLUME_NORM (0x10000) + +/** Muted volume (0%) */ +#define PA_VOLUME_MUTED (0) + +/** A structure encapsulating a per-channel volume */ +typedef struct pa_cvolume { + uint8_t channels; + pa_volume_t values[PA_CHANNELS_MAX]; +} pa_cvolume; + +/** Return non-zero when *a == *b */ +int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b); + +/** Set the volume of all channels to PA_VOLUME_NORM */ +#define pa_cvolume_reset(a, n) pa_cvolume_set((a), (n), PA_VOLUME_NORM) + +/** Set the volume of all channels to PA_VOLUME_MUTED */ +#define pa_cvolume_mute(a, n) pa_cvolume_set((a), (n), PA_VOLUME_MUTED) + +/** Set the volume of all channels to the specified parameter */ +pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v); + +/** Pretty print a volume structure */ +#define PA_CVOLUME_SNPRINT_MAX 64 +char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c); + +/** Return the average volume of all channels */ +pa_volume_t pa_cvolume_avg(const pa_cvolume *a); + +/** Return TRUE when the passed cvolume structure is valid, FALSE otherwise */ +int pa_cvolume_valid(const pa_cvolume *v); + +/** Return non-zero if the volume of all channels is equal to the specified value */ +int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v); + +#define pa_cvolume_is_muted(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_MUTED) +#define pa_cvolume_is_norm(a) pa_cvolume_channels_equal_to((a), PA_VOLUME_NORM) + +/** Multiply two volumes specifications, return the result. This uses PA_VOLUME_NORM as neutral element of multiplication. */ +pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b); + +pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b); + +/** Convert a decibel value to a volume. \since 0.4 */ +pa_volume_t pa_sw_volume_from_dB(double f); + +/** Convert a volume to a decibel value. \since 0.4 */ +double pa_sw_volume_to_dB(pa_volume_t v); + +/** Convert a linear factor to a volume. \since 0.8 */ +pa_volume_t pa_sw_volume_from_linear(double v); + +/** Convert a volume to a linear factor. \since 0.8 */ +double pa_sw_volume_to_linear(pa_volume_t v); + +#ifdef INFINITY +#define PA_DECIBEL_MININFTY (-INFINITY) +#else +/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). \since 0.4 */ +#define PA_DECIBEL_MININFTY (-200) +#endif + +PA_C_DECL_END + +#endif diff --git a/src/polypcore/alsa-util.c b/src/polypcore/alsa-util.c new file mode 100644 index 00000000..7528ee0b --- /dev/null +++ b/src/polypcore/alsa-util.c @@ -0,0 +1,124 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "alsa-util.h" +#include +#include "xmalloc.h" +#include "log.h" + +/* Set the hardware parameters of the given ALSA device. Returns the + * selected fragment settings in *period and *period_size */ +int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, const pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size) { + int ret = -1; + snd_pcm_uframes_t buffer_size; + snd_pcm_hw_params_t *hwparams = NULL; + static const snd_pcm_format_t format_trans[] = { + [PA_SAMPLE_U8] = SND_PCM_FORMAT_U8, + [PA_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW, + [PA_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW, + [PA_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE, + [PA_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE, + [PA_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE, + [PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE, + }; + assert(pcm_handle && ss && periods && period_size); + + if (snd_pcm_hw_params_malloc(&hwparams) < 0 || + snd_pcm_hw_params_any(pcm_handle, hwparams) < 0 || + snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 || + snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[ss->format]) < 0 || + snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &ss->rate, NULL) < 0 || + snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss->channels) < 0 || + (*periods > 0 && snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, periods, NULL) < 0) || + (*period_size > 0 && snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, period_size, NULL) < 0) || + snd_pcm_hw_params(pcm_handle, hwparams) < 0) + goto finish; + + if (snd_pcm_prepare(pcm_handle) < 0) + goto finish; + + if (snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size) < 0 || + snd_pcm_hw_params_get_period_size(hwparams, period_size, NULL) < 0) + goto finish; + + assert(buffer_size > 0); + assert(*period_size > 0); + *periods = buffer_size / *period_size; + assert(*periods > 0); + + ret = 0; + +finish: + if (hwparams) + snd_pcm_hw_params_free(hwparams); + + return ret; +} + +/* Allocate an IO event for every ALSA poll descriptor for the + * specified ALSA device. Return a pointer to such an array in + * *io_events. Store the length of that array in *n_io_events. Use the + * specified callback function and userdata. The array has to be freed + * with pa_free_io_events(). */ +int pa_create_io_events(snd_pcm_t *pcm_handle, pa_mainloop_api* m, pa_io_event ***io_events, unsigned *n_io_events, void (*cb)(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata), void *userdata) { + unsigned i; + struct pollfd *pfds, *ppfd; + pa_io_event **ios; + assert(pcm_handle && m && io_events && n_io_events && cb); + + *n_io_events = snd_pcm_poll_descriptors_count(pcm_handle); + + pfds = pa_xmalloc(sizeof(struct pollfd) * *n_io_events); + if (snd_pcm_poll_descriptors(pcm_handle, pfds, *n_io_events) < 0) { + pa_xfree(pfds); + return -1; + } + + *io_events = pa_xmalloc(sizeof(void*) * *n_io_events); + + for (i = 0, ios = *io_events, ppfd = pfds; i < *n_io_events; i++, ios++, ppfd++) { + *ios = m->io_new(m, ppfd->fd, + ((ppfd->events & POLLIN) ? PA_IO_EVENT_INPUT : 0) | + ((ppfd->events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0), cb, userdata); + assert(*ios); + } + + pa_xfree(pfds); + return 0; +} + +/* Free the memory allocated by pa_create_io_events() */ +void pa_free_io_events(pa_mainloop_api* m, pa_io_event **io_events, unsigned n_io_events) { + unsigned i; + pa_io_event **ios; + assert(m && io_events); + + for (ios = io_events, i = 0; i < n_io_events; i++, ios++) + m->io_free(*ios); + pa_xfree(io_events); +} diff --git a/src/polypcore/alsa-util.h b/src/polypcore/alsa-util.h new file mode 100644 index 00000000..5d6d6634 --- /dev/null +++ b/src/polypcore/alsa-util.h @@ -0,0 +1,35 @@ +#ifndef fooalsautilhfoo +#define fooalsautilhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include + +int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, const pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size); + +int pa_create_io_events(snd_pcm_t *pcm_handle, pa_mainloop_api *m, pa_io_event ***io_events, unsigned *n_io_events, void (*cb)(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata), void *userdata); +void pa_free_io_events(pa_mainloop_api* m, pa_io_event **io_sources, unsigned n_io_sources); + +#endif diff --git a/src/polypcore/authkey-prop.c b/src/polypcore/authkey-prop.c new file mode 100644 index 00000000..8657f5a5 --- /dev/null +++ b/src/polypcore/authkey-prop.c @@ -0,0 +1,87 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +#include "xmalloc.h" +#include "authkey-prop.h" +#include "props.h" +#include "log.h" + +struct authkey_data { + int ref; + size_t length; +}; + +int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len) { + struct authkey_data *a; + assert(c && name && data && len > 0); + + if (!(a = pa_property_get(c, name))) + return -1; + + assert(a->length == len); + memcpy(data, a+1, len); + return 0; +} + +int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len) { + struct authkey_data *a; + assert(c && name); + + if (pa_property_get(c, name)) + return -1; + + a = pa_xmalloc(sizeof(struct authkey_data) + len); + a->ref = 1; + a->length = len; + memcpy(a+1, data, len); + + pa_property_set(c, name, a); + + return 0; +} + +void pa_authkey_prop_ref(pa_core *c, const char *name) { + struct authkey_data *a; + assert(c && name); + + a = pa_property_get(c, name); + assert(a && a->ref >= 1); + + a->ref++; +} + +void pa_authkey_prop_unref(pa_core *c, const char *name) { + struct authkey_data *a; + assert(c && name); + + a = pa_property_get(c, name); + assert(a && a->ref >= 1); + + if (!(--a->ref)) { + pa_property_remove(c, name); + pa_xfree(a); + } +} + + diff --git a/src/polypcore/authkey-prop.h b/src/polypcore/authkey-prop.h new file mode 100644 index 00000000..29b40bb2 --- /dev/null +++ b/src/polypcore/authkey-prop.h @@ -0,0 +1,43 @@ +#ifndef fooauthkeyprophfoo +#define fooauthkeyprophfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "core.h" + +/* The authkey-prop uses a central property to store a previously + * loaded cookie in memory. Useful for sharing the same cookie between + * several modules. */ + +/* Return the data of the specified authorization key property. Doesn't alter the refernce count of the key */ +int pa_authkey_prop_get(pa_core *c, const char *name, void *data, size_t len); + +/* Store data in the specified authorization key property. The initial reference count is set to 1 */ +int pa_authkey_prop_put(pa_core *c, const char *name, const void *data, size_t len); + +/* Increase the reference count of the specified authorization key */ +void pa_authkey_prop_ref(pa_core *c, const char *name); + +/* Decrease the reference count of the specified authorization key */ +void pa_authkey_prop_unref(pa_core *c, const char *name); + +#endif diff --git a/src/polypcore/authkey.c b/src/polypcore/authkey.c new file mode 100644 index 00000000..969f09d9 --- /dev/null +++ b/src/polypcore/authkey.c @@ -0,0 +1,201 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "authkey.h" +#include "util.h" +#include "log.h" +#include "random.h" + +/* Generate a new authorization key, store it in file fd and return it in *data */ +static int generate(int fd, void *ret_data, size_t length) { + ssize_t r; + assert(fd >= 0 && ret_data && length); + + pa_random(ret_data, length); + + lseek(fd, 0, SEEK_SET); + ftruncate(fd, 0); + + if ((r = pa_loop_write(fd, ret_data, length)) < 0 || (size_t) r != length) { + pa_log(__FILE__": failed to write cookie file: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +/* Load an euthorization cookie from file fn and store it in data. If + * the cookie file doesn't exist, create it */ +static int load(const char *fn, void *data, size_t length) { + int fd = -1; + int writable = 1; + int unlock = 0, ret = -1; + ssize_t r; + assert(fn && data && length); + + if ((fd = open(fn, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) { + if (errno != EACCES || (fd = open(fn, O_RDONLY)) < 0) { + pa_log(__FILE__": failed to open cookie file '%s': %s\n", fn, strerror(errno)); + goto finish; + } else + writable = 0; + } + + unlock = pa_lock_fd(fd, 1) >= 0; + + if ((r = pa_loop_read(fd, data, length)) < 0) { + pa_log(__FILE__": failed to read cookie file '%s': %s\n", fn, strerror(errno)); + goto finish; + } + + if ((size_t) r != length) { + + if (!writable) { + pa_log(__FILE__": unable to write cookie to read only file\n"); + goto finish; + } + + if (generate(fd, data, length) < 0) + goto finish; + } + + ret = 0; + +finish: + + if (fd >= 0) { + + if (unlock) + pa_lock_fd(fd, 0); + + close(fd); + } + + return ret; +} + +/* Load a cookie from a cookie file. If the file doesn't exist, create it. */ +int pa_authkey_load(const char *path, void *data, size_t length) { + int ret; + + assert(path && data && length); + + ret = load(path, data, length); + + if (ret < 0) + pa_log(__FILE__": Failed to load authorization key '%s': %s\n", path, + (ret == -1) ? strerror(errno) : "file corrupt"); + + return ret; +} + +/* If the specified file path starts with / return it, otherwise + * return path prepended with home directory */ +static const char *normalize_path(const char *fn, char *s, size_t l) { + assert(fn && s && l > 0); + +#ifndef OS_IS_WIN32 + if (fn[0] != '/') { +#else + if (strlen(fn) < 3 || !isalpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') { +#endif + char homedir[PATH_MAX]; + if (!pa_get_home_dir(homedir, sizeof(homedir))) + return NULL; + +#ifndef OS_IS_WIN32 + snprintf(s, l, "%s/%s", homedir, fn); +#else + snprintf(s, l, "%s\\%s", homedir, fn); +#endif + return s; + } + + return fn; +} + +/* Load a cookie from a file in the home directory. If the specified + * path starts with /, use it as absolute path instead. */ +int pa_authkey_load_auto(const char *fn, void *data, size_t length) { + char path[PATH_MAX]; + const char *p; + assert(fn && data && length); + + if (!(p = normalize_path(fn, path, sizeof(path)))) + return -2; + + return pa_authkey_load(p, data, length); +} + +/* Store the specified cookie in the speicified cookie file */ +int pa_authkey_save(const char *fn, const void *data, size_t length) { + int fd = -1; + int unlock = 0, ret = -1; + ssize_t r; + char path[PATH_MAX]; + const char *p; + assert(fn && data && length); + + if (!(p = normalize_path(fn, path, sizeof(path)))) + return -2; + + if ((fd = open(p, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) { + pa_log(__FILE__": failed to open cookie file '%s': %s\n", fn, strerror(errno)); + goto finish; + } + + unlock = pa_lock_fd(fd, 1) >= 0; + + if ((r = pa_loop_write(fd, data, length)) < 0 || (size_t) r != length) { + pa_log(__FILE__": failed to read cookie file '%s': %s\n", fn, strerror(errno)); + goto finish; + } + + ret = 0; + +finish: + + if (fd >= 0) { + + if (unlock) + pa_lock_fd(fd, 0); + + close(fd); + } + + return ret; +} diff --git a/src/polypcore/authkey.h b/src/polypcore/authkey.h new file mode 100644 index 00000000..81b1a578 --- /dev/null +++ b/src/polypcore/authkey.h @@ -0,0 +1,32 @@ +#ifndef fooauthkeyhfoo +#define fooauthkeyhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +int pa_authkey_load(const char *path, void *data, size_t len); +int pa_authkey_load_auto(const char *fn, void *data, size_t length); + +int pa_authkey_save(const char *path, const void *data, size_t length); + +#endif diff --git a/src/polypcore/autoload.c b/src/polypcore/autoload.c new file mode 100644 index 00000000..ff2916cb --- /dev/null +++ b/src/polypcore/autoload.c @@ -0,0 +1,180 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "autoload.h" +#include "module.h" +#include "xmalloc.h" +#include "memchunk.h" +#include "sound-file.h" +#include "log.h" +#include "scache.h" +#include "subscribe.h" + +static void entry_free(pa_autoload_entry *e) { + assert(e); + pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_REMOVE, PA_INVALID_INDEX); + pa_xfree(e->name); + pa_xfree(e->module); + pa_xfree(e->argument); + pa_xfree(e); +} + +static void entry_remove_and_free(pa_autoload_entry *e) { + assert(e && e->core); + + pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL); + pa_hashmap_remove(e->core->autoload_hashmap, e->name); + entry_free(e); +} + +static pa_autoload_entry* entry_new(pa_core *c, const char *name) { + pa_autoload_entry *e = NULL; + assert(c && name); + + if (c->autoload_hashmap && (e = pa_hashmap_get(c->autoload_hashmap, name))) + return NULL; + + e = pa_xmalloc(sizeof(pa_autoload_entry)); + e->core = c; + e->name = pa_xstrdup(name); + e->module = e->argument = NULL; + e->in_action = 0; + + if (!c->autoload_hashmap) + c->autoload_hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + assert(c->autoload_hashmap); + + pa_hashmap_put(c->autoload_hashmap, e->name, e); + + if (!c->autoload_idxset) + c->autoload_idxset = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + pa_idxset_put(c->autoload_idxset, e, &e->index); + + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_NEW, e->index); + + return e; +} + +int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx) { + pa_autoload_entry *e = NULL; + assert(c && name && module && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE)); + + if (!(e = entry_new(c, name))) + return -1; + + e->module = pa_xstrdup(module); + e->argument = pa_xstrdup(argument); + e->type = type; + + if (idx) + *idx = e->index; + + return 0; +} + +int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type) { + pa_autoload_entry *e; + assert(c && name && type); + + if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type) + return -1; + + entry_remove_and_free(e); + return 0; +} + +int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) { + pa_autoload_entry *e; + assert(c && idx != PA_IDXSET_INVALID); + + if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx))) + return -1; + + entry_remove_and_free(e); + return 0; +} + +void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) { + pa_autoload_entry *e; + pa_module *m; + assert(c && name); + + if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || (e->type != type)) + return; + + if (e->in_action) + return; + + e->in_action = 1; + + if (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) { + if ((m = pa_module_load(c, e->module, e->argument))) + m->auto_unload = 1; + } + + e->in_action = 0; +} + +static void free_func(void *p, PA_GCC_UNUSED void *userdata) { + pa_autoload_entry *e = p; + pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL); + entry_free(e); +} + +void pa_autoload_free(pa_core *c) { + if (c->autoload_hashmap) { + pa_hashmap_free(c->autoload_hashmap, free_func, NULL); + c->autoload_hashmap = NULL; + } + + if (c->autoload_idxset) { + pa_idxset_free(c->autoload_idxset, NULL, NULL); + c->autoload_idxset = NULL; + } +} + +const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type) { + pa_autoload_entry *e; + assert(c && name); + + if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type) + return NULL; + + return e; +} + +const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx) { + pa_autoload_entry *e; + assert(c && idx != PA_IDXSET_INVALID); + + if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx))) + return NULL; + + return e; +} diff --git a/src/polypcore/autoload.h b/src/polypcore/autoload.h new file mode 100644 index 00000000..7350c16a --- /dev/null +++ b/src/polypcore/autoload.h @@ -0,0 +1,58 @@ +#ifndef fooautoloadhfoo +#define fooautoloadhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "namereg.h" + +/* Using the autoloading facility, modules by be loaded on-demand and + * synchronously. The user may register a "ghost sink" or "ghost + * source". Whenever this sink/source is requested but not available a + * specified module is loaded. */ + +/* An autoload entry, or "ghost" sink/source */ +typedef struct pa_autoload_entry { + pa_core *core; + uint32_t index; + char *name; + pa_namereg_type_t type; /* Type of the autoload entry */ + int in_action; /* Currently loaded */ + char *module, *argument; +} pa_autoload_entry; + +/* Add a new autoload entry of the given time, with the speicified + * sink/source name, module name and argument. Return the entry's + * index in *index */ +int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx); + +/* Free all autoload entries */ +void pa_autoload_free(pa_core *c); +int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type); +int pa_autoload_remove_by_index(pa_core *c, uint32_t idx); + +/* Request an autoload entry by its name, effectively causing a module to be loaded */ +void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type); + +const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type); +const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx); + +#endif diff --git a/src/polypcore/cli-command.c b/src/polypcore/cli-command.c new file mode 100644 index 00000000..f6192bf8 --- /dev/null +++ b/src/polypcore/cli-command.c @@ -0,0 +1,834 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "cli-command.h" +#include "module.h" +#include "sink.h" +#include "source.h" +#include "client.h" +#include "sink-input.h" +#include "source-output.h" +#include "tokenizer.h" +#include "strbuf.h" +#include "namereg.h" +#include "cli-text.h" +#include "scache.h" +#include "sample-util.h" +#include "sound-file.h" +#include "play-memchunk.h" +#include "autoload.h" +#include "xmalloc.h" +#include "sound-file-stream.h" +#include "props.h" +#include "util.h" + +struct command { + const char *name; + int (*proc) (pa_core *c, pa_tokenizer*t, pa_strbuf *buf, int *fail); + const char *help; + unsigned args; +}; + +#define INCLUDE_META ".include" +#define FAIL_META ".fail" +#define NOFAIL_META ".nofail" + +/* Prototypes for all available commands */ +static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); +static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail); + + +/* A method table for all available commands */ + +static const struct command commands[] = { + { "exit", pa_cli_command_exit, "Terminate the daemon", 1 }, + { "help", pa_cli_command_help, "Show this help", 1 }, + { "list-modules", pa_cli_command_modules, "List loaded modules", 1 }, + { "list-sinks", pa_cli_command_sinks, "List loaded sinks", 1 }, + { "list-sources", pa_cli_command_sources, "List loaded sources", 1 }, + { "list-clients", pa_cli_command_clients, "List loaded clients", 1 }, + { "list-sink-inputs", pa_cli_command_sink_inputs, "List sink inputs", 1 }, + { "list-source-outputs", pa_cli_command_source_outputs, "List source outputs", 1 }, + { "stat", pa_cli_command_stat, "Show memory block statistics", 1 }, + { "info", pa_cli_command_info, "Show comprehensive status", 1 }, + { "ls", pa_cli_command_info, NULL, 1 }, + { "list", pa_cli_command_info, NULL, 1 }, + { "load-module", pa_cli_command_load, "Load a module (args: name, arguments)", 3}, + { "unload-module", pa_cli_command_unload, "Unload a module (args: index)", 2}, + { "set-sink-volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3}, + { "set-sink-input-volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index|name, volume)", 3}, + { "set-default-sink", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2}, + { "set-default-source", pa_cli_command_source_default, "Set the default source (args: index|name)", 2}, + { "kill-client", pa_cli_command_kill_client, "Kill a client (args: index)", 2}, + { "kill-sink-input", pa_cli_command_kill_sink_input, "Kill a sink input (args: index)", 2}, + { "kill-source-output", pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2}, + { "list-samples", pa_cli_command_scache_list, "List all entries in the sample cache", 1}, + { "play-sample", pa_cli_command_scache_play, "Play a sample from the sample cache (args: name, sink|index)", 3}, + { "remove-sample", pa_cli_command_scache_remove, "Remove a sample from the sample cache (args: name)", 2}, + { "load-sample", pa_cli_command_scache_load, "Load a sound file into the sample cache (args: name, filename)", 3}, + { "load-sample-lazy", pa_cli_command_scache_load, "Lazily load a sound file into the sample cache (args: name, filename)", 3}, + { "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2}, + { "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3}, + { "list-autoload", pa_cli_command_autoload_list, "List autoload entries", 1}, + { "add-autoload-sink", pa_cli_command_autoload_add, "Add autoload entry for a sink (args: sink, module name, arguments)", 4}, + { "add-autoload-source", pa_cli_command_autoload_add, "Add autoload entry for a source (args: source, module name, arguments)", 4}, + { "remove-autoload-sink", pa_cli_command_autoload_remove, "Remove autoload entry for a sink (args: name)", 2}, + { "remove-autoload-source", pa_cli_command_autoload_remove, "Remove autoload entry for a source (args: name)", 2}, + { "dump", pa_cli_command_dump, "Dump daemon configuration", 1}, + { "list-props", pa_cli_command_list_props, NULL, 1}, + { NULL, NULL, NULL, 0 } +}; + +static const char whitespace[] = " \t\n\r"; +static const char linebreak[] = "\n\r"; + +static uint32_t parse_index(const char *n) { + uint32_t idx; + + if (pa_atou(n, &idx) < 0) + return (uint32_t) PA_IDXSET_INVALID; + + return idx; +} + +static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, PA_GCC_UNUSED pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + assert(c && c->mainloop && t); + c->mainloop->quit(c->mainloop, 0); + return 0; +} + +static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const struct command*command; + assert(c && t && buf); + + pa_strbuf_puts(buf, "Available commands:\n"); + + for (command = commands; command->name; command++) + if (command->help) + pa_strbuf_printf(buf, " %-25s %s\n", command->name, command->help); + return 0; +} + +static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char *s; + assert(c && t); + s = pa_module_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + +static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char *s; + assert(c && t); + s = pa_client_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + +static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char *s; + assert(c && t); + s = pa_sink_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + +static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char *s; + assert(c && t); + s = pa_source_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + +static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char *s; + assert(c && t); + s = pa_sink_input_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + +static int pa_cli_command_source_outputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char *s; + assert(c && t); + s = pa_source_output_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + +static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char s[256]; + assert(c && t); + + pa_bytes_snprint(s, sizeof(s), c->memblock_stat->total_size); + pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n", + c->memblock_stat->total, + s); + + pa_bytes_snprint(s, sizeof(s), c->memblock_stat->allocated_size); + pa_strbuf_printf(buf, "Memory blocks allocated during the whole lifetime: %u, size: %s.\n", + c->memblock_stat->allocated, + s); + + pa_bytes_snprint(s, sizeof(s), pa_scache_total_size(c)); + pa_strbuf_printf(buf, "Total sample cache size: %s.\n", s); + + pa_sample_spec_snprint(s, sizeof(s), &c->default_sample_spec); + pa_strbuf_printf(buf, "Default sample spec: %s\n", s); + + pa_strbuf_printf(buf, "Default sink name: %s\n" + "Default source name: %s\n", + pa_namereg_get_default_sink_name(c), + pa_namereg_get_default_source_name(c)); + + return 0; +} + +static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { + assert(c && t); + pa_cli_command_stat(c, t, buf, fail); + pa_cli_command_modules(c, t, buf, fail); + pa_cli_command_sinks(c, t, buf, fail); + pa_cli_command_sources(c, t, buf, fail); + pa_cli_command_clients(c, t, buf, fail); + pa_cli_command_sink_inputs(c, t, buf, fail); + pa_cli_command_source_outputs(c, t, buf, fail); + pa_cli_command_scache_list(c, t, buf, fail); + pa_cli_command_autoload_list(c, t, buf, fail); + return 0; +} + +static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + pa_module *m; + const char *name; + assert(c && t); + + if (!(name = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n"); + return -1; + } + + if (!(m = pa_module_load(c, name, pa_tokenizer_get(t, 2)))) { + pa_strbuf_puts(buf, "Module load failed.\n"); + return -1; + } + + return 0; +} + +static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + pa_module *m; + uint32_t idx; + const char *i; + char *e; + assert(c && t); + + if (!(i = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify the module index.\n"); + return -1; + } + + idx = (uint32_t) strtoul(i, &e, 10); + if (*e || !(m = pa_idxset_get_by_index(c->modules, idx))) { + pa_strbuf_puts(buf, "Invalid module index.\n"); + return -1; + } + + pa_module_unload_request(m); + return 0; +} + +static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n, *v; + pa_sink *sink; + uint32_t volume; + pa_cvolume cvolume; + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n"); + return -1; + } + + if (!(v = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); + return -1; + } + + if (pa_atou(v, &volume) < 0) { + pa_strbuf_puts(buf, "Failed to parse volume.\n"); + return -1; + } + + if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) { + pa_strbuf_puts(buf, "No sink found by this name or index.\n"); + return -1; + } + + pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume); + pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &cvolume); + return 0; +} + +static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n, *v; + pa_sink_input *si; + pa_volume_t volume; + pa_cvolume cvolume; + uint32_t idx; + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n"); + return -1; + } + + if ((idx = parse_index(n)) == PA_IDXSET_INVALID) { + pa_strbuf_puts(buf, "Failed to parse index.\n"); + return -1; + } + + if (!(v = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); + return -1; + } + + if (pa_atou(v, &volume) < 0) { + pa_strbuf_puts(buf, "Failed to parse volume.\n"); + return -1; + } + + if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) { + pa_strbuf_puts(buf, "No sink input found with this index.\n"); + return -1; + } + + pa_cvolume_set(&cvolume, si->sample_spec.channels, volume); + pa_sink_input_set_volume(si, &cvolume); + return 0; +} + +static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n; + assert(c && t); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n"); + return -1; + } + + pa_namereg_set_default(c, n, PA_NAMEREG_SINK); + return 0; +} + +static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n; + assert(c && t); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n"); + return -1; + } + + pa_namereg_set_default(c, n, PA_NAMEREG_SOURCE); + return 0; +} + +static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n; + pa_client *client; + uint32_t idx; + assert(c && t); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a client by its index.\n"); + return -1; + } + + if ((idx = parse_index(n)) == PA_IDXSET_INVALID) { + pa_strbuf_puts(buf, "Failed to parse index.\n"); + return -1; + } + + if (!(client = pa_idxset_get_by_index(c->clients, idx))) { + pa_strbuf_puts(buf, "No client found by this index.\n"); + return -1; + } + + pa_client_kill(client); + return 0; +} + +static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n; + pa_sink_input *sink_input; + uint32_t idx; + assert(c && t); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n"); + return -1; + } + + if ((idx = parse_index(n)) == PA_IDXSET_INVALID) { + pa_strbuf_puts(buf, "Failed to parse index.\n"); + return -1; + } + + if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx))) { + pa_strbuf_puts(buf, "No sink input found by this index.\n"); + return -1; + } + + pa_sink_input_kill(sink_input); + return 0; +} + +static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + const char *n; + pa_source_output *source_output; + uint32_t idx; + assert(c && t); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a source output by its index.\n"); + return -1; + } + + if ((idx = parse_index(n)) == PA_IDXSET_INVALID) { + pa_strbuf_puts(buf, "Failed to parse index.\n"); + return -1; + } + + if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx))) { + pa_strbuf_puts(buf, "No source output found by this index.\n"); + return -1; + } + + pa_source_output_kill(source_output); + return 0; +} + +static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char *s; + assert(c && t); + s = pa_scache_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + +static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { + const char *n, *sink_name; + pa_sink *sink; + assert(c && t && buf && fail); + + if (!(n = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a sample name and a sink name.\n"); + return -1; + } + + if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) { + pa_strbuf_puts(buf, "No sink by that name.\n"); + return -1; + } + + if (pa_scache_play_item(c, n, sink, NULL) < 0) { + pa_strbuf_puts(buf, "Failed to play sample.\n"); + return -1; + } + + return 0; +} + +static int pa_cli_command_scache_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { + const char *n; + assert(c && t && buf && fail); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sample name.\n"); + return -1; + } + + if (pa_scache_remove_item(c, n) < 0) { + pa_strbuf_puts(buf, "Failed to remove sample.\n"); + return -1; + } + + return 0; +} + +static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { + const char *fname, *n; + int r; + assert(c && t && buf && fail); + + if (!(fname = pa_tokenizer_get(t, 2)) || !(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a file name and a sample name.\n"); + return -1; + } + + if (strstr(pa_tokenizer_get(t, 0), "lazy")) + r = pa_scache_add_file_lazy(c, n, fname, NULL); + else + r = pa_scache_add_file(c, n, fname, NULL); + + if (r < 0) + pa_strbuf_puts(buf, "Failed to load sound file.\n"); + + return 0; +} + +static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { + const char *pname; + assert(c && t && buf && fail); + + if (!(pname = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a path name.\n"); + return -1; + } + + if (pa_scache_add_directory_lazy(c, pname) < 0) { + pa_strbuf_puts(buf, "Failed to load directory.\n"); + return -1; + } + + return 0; +} + +static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { + const char *fname, *sink_name; + pa_sink *sink; + assert(c && t && buf && fail); + + if (!(fname = pa_tokenizer_get(t, 1)) || !(sink_name = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a file name and a sink name.\n"); + return -1; + } + + if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) { + pa_strbuf_puts(buf, "No sink by that name.\n"); + return -1; + } + + + return pa_play_file(sink, fname, NULL); +} + +static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { + const char *a, *b; + assert(c && t && buf && fail); + + if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n"); + return -1; + } + + pa_autoload_add(c, a, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, b, pa_tokenizer_get(t, 3), NULL); + + return 0; +} + +static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, int *fail) { + const char *name; + assert(c && t && buf && fail); + + if (!(name = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a device name\n"); + return -1; + } + + if (pa_autoload_remove_by_name(c, name, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE) < 0) { + pa_strbuf_puts(buf, "Failed to remove autload entry\n"); + return -1; + } + + return 0; +} + +static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + char *s; + assert(c && t); + s = pa_autoload_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + pa_xfree(s); + return 0; +} + +static int pa_cli_command_list_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + assert(c && t); + pa_property_dump(c, buf); + return 0; +} + +static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, PA_GCC_UNUSED int *fail) { + pa_module *m; + pa_sink *s; + int nl; + const char *p; + uint32_t idx; + char txt[256]; + time_t now; + void *i; + pa_autoload_entry *a; + + assert(c && t); + + time(&now); + +#ifdef HAVE_CTIME_R + pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime_r(&now, txt)); +#else + pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now)); +#endif + + + for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) { + if (m->auto_unload) + continue; + + pa_strbuf_printf(buf, "load-module %s", m->name); + + if (m->argument) + pa_strbuf_printf(buf, " %s", m->argument); + + pa_strbuf_puts(buf, "\n"); + } + + nl = 0; + + for (s = pa_idxset_first(c->sinks, &idx); s; s = pa_idxset_next(c->sinks, &idx)) { + if (s->owner && s->owner->auto_unload) + continue; + + if (!nl) { + pa_strbuf_puts(buf, "\n"); + nl = 1; + } + + pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", s->name, pa_cvolume_avg(pa_sink_get_volume(s, PA_MIXER_HARDWARE))); + } + + + if (c->autoload_hashmap) { + nl = 0; + + i = NULL; + while ((a = pa_hashmap_iterate(c->autoload_hashmap, &i, NULL))) { + + if (!nl) { + pa_strbuf_puts(buf, "\n"); + nl = 1; + } + + pa_strbuf_printf(buf, "add-autoload-%s %s %s", a->type == PA_NAMEREG_SINK ? "sink" : "source", a->name, a->module); + + if (a->argument) + pa_strbuf_printf(buf, " %s", a->argument); + + pa_strbuf_puts(buf, "\n"); + } + } + + nl = 0; + + if ((p = pa_namereg_get_default_sink_name(c))) { + if (!nl) { + pa_strbuf_puts(buf, "\n"); + nl = 1; + } + pa_strbuf_printf(buf, "set-default-sink %s\n", p); + } + + if ((p = pa_namereg_get_default_source_name(c))) { + if (!nl) { + pa_strbuf_puts(buf, "\n"); + nl = 1; + } + pa_strbuf_printf(buf, "set-default-source %s\n", p); + } + + pa_strbuf_puts(buf, "\n### EOF\n"); + + return 0; +} + + +int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail) { + const char *cs; + + cs = s+strspn(s, whitespace); + + if (*cs == '#' || !*cs) + return 0; + else if (*cs == '.') { + if (!strcmp(cs, FAIL_META)) + *fail = 1; + else if (!strcmp(cs, NOFAIL_META)) + *fail = 0; + else { + size_t l; + l = strcspn(cs, whitespace); + + if (l == sizeof(INCLUDE_META)-1 && !strncmp(cs, INCLUDE_META, l)) { + const char *filename = cs+l+strspn(cs+l, whitespace); + + if (pa_cli_command_execute_file(c, filename, buf, fail) < 0) + if (*fail) return -1; + } else { + pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs); + if (*fail) return -1; + } + } + } else { + const struct command*command; + int unknown = 1; + size_t l; + + l = strcspn(cs, whitespace); + + for (command = commands; command->name; command++) + if (strlen(command->name) == l && !strncmp(cs, command->name, l)) { + int ret; + pa_tokenizer *t = pa_tokenizer_new(cs, command->args); + assert(t); + ret = command->proc(c, t, buf, fail); + pa_tokenizer_free(t); + unknown = 0; + + if (ret < 0 && *fail) + return -1; + + break; + } + + if (unknown) { + pa_strbuf_printf(buf, "Unknown command: %s\n", cs); + if (*fail) + return -1; + } + } + + return 0; +} + +int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail) { + char line[256]; + FILE *f = NULL; + int ret = -1; + assert(c && fn && buf); + + if (!(f = fopen(fn, "r"))) { + pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, strerror(errno)); + if (!*fail) + ret = 0; + goto fail; + } + + while (fgets(line, sizeof(line), f)) { + char *e = line + strcspn(line, linebreak); + *e = 0; + + if (pa_cli_command_execute_line(c, line, buf, fail) < 0 && *fail) + goto fail; + } + + ret = 0; + +fail: + if (f) + fclose(f); + + return ret; +} + +int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail) { + const char *p; + assert(c && s && buf && fail); + + p = s; + while (*p) { + size_t l = strcspn(p, linebreak); + char *line = pa_xstrndup(p, l); + + if (pa_cli_command_execute_line(c, line, buf, fail) < 0&& *fail) { + pa_xfree(line); + return -1; + } + pa_xfree(line); + + p += l; + p += strspn(p, linebreak); + } + + return 0; +} diff --git a/src/polypcore/cli-command.h b/src/polypcore/cli-command.h new file mode 100644 index 00000000..78b8d5c6 --- /dev/null +++ b/src/polypcore/cli-command.h @@ -0,0 +1,40 @@ +#ifndef fooclicommandhfoo +#define fooclicommandhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "strbuf.h" +#include "core.h" + +/* Execute a single CLI command. Write the results to the string + * buffer *buf. If *fail is non-zero the function will return -1 when + * one or more of the executed commands failed. *fail + * may be modified by the function call. */ +int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, int *fail); + +/* Execute a whole file of CLI commands */ +int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, int *fail); + +/* Split the specified string into lines and run pa_cli_command_execute_line() for each. */ +int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, int *fail); + +#endif diff --git a/src/polypcore/cli-text.c b/src/polypcore/cli-text.c new file mode 100644 index 00000000..58248d8e --- /dev/null +++ b/src/polypcore/cli-text.c @@ -0,0 +1,385 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "cli-text.h" +#include "module.h" +#include "client.h" +#include "sink.h" +#include "source.h" +#include "sink-input.h" +#include "source-output.h" +#include "strbuf.h" +#include "sample-util.h" +#include "scache.h" +#include "autoload.h" +#include "xmalloc.h" +#include + +char *pa_module_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_module *m; + uint32_t idx = PA_IDXSET_INVALID; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(c->modules)); + + for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) + pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\targument: <%s>\n\tused: %i\n\tauto unload: %s\n", m->index, m->name, m->argument, m->n_used, m->auto_unload ? "yes" : "no"); + + return pa_strbuf_tostring_free(s); +} + +char *pa_client_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_client *client; + uint32_t idx = PA_IDXSET_INVALID; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients)); + + for (client = pa_idxset_first(c->clients, &idx); client; client = pa_idxset_next(c->clients, &idx)) { + pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\tdriver: <%s>\n", client->index, client->name, client->driver); + + if (client->owner) + pa_strbuf_printf(s, "\towner module: <%u>\n", client->owner->index); + } + + return pa_strbuf_tostring_free(s); +} + +char *pa_sink_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_sink *sink; + uint32_t idx = PA_IDXSET_INVALID; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks)); + + for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) { + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + + pa_strbuf_printf( + s, + " %c index: %u\n" + "\tname: <%s>\n" + "\tdriver: <%s>\n" + "\tvolume: <%s>\n" + "\tlatency: <%0.0f usec>\n" + "\tmonitor_source: <%u>\n" + "\tsample spec: <%s>\n" + "\tchannel map: <%s>\n", + c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ', + sink->index, sink->name, + sink->driver, + pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, PA_MIXER_HARDWARE)), + (double) pa_sink_get_latency(sink), + sink->monitor_source->index, + pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec), + pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map)); + + if (sink->owner) + pa_strbuf_printf(s, "\towner module: <%u>\n", sink->owner->index); + if (sink->description) + pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description); + } + + return pa_strbuf_tostring_free(s); +} + +char *pa_source_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_source *source; + uint32_t idx = PA_IDXSET_INVALID; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources)); + + for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) { + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + + + pa_strbuf_printf( + s, + " %c index: %u\n" + "\tname: <%s>\n" + "\tdriver: <%s>\n" + "\tlatency: <%0.0f usec>\n" + "\tsample spec: <%s>\n" + "\tchannel map: <%s>\n", + c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ', + source->index, + source->name, + source->driver, + (double) pa_source_get_latency(source), + pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec), + pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map)); + + if (source->monitor_of) + pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index); + if (source->owner) + pa_strbuf_printf(s, "\towner module: <%u>\n", source->owner->index); + if (source->description) + pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description); + } + + return pa_strbuf_tostring_free(s); +} + + +char *pa_source_output_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_source_output *o; + uint32_t idx = PA_IDXSET_INVALID; + static const char* const state_table[] = { + "RUNNING", + "CORKED", + "DISCONNECTED" + }; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs)); + + for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) { + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + + assert(o->source); + + pa_strbuf_printf( + s, + " index: %u\n" + "\tname: '%s'\n" + "\tdriver: <%s>\n" + "\tstate: %s\n" + "\tsource: <%u> '%s'\n" + "\tsample spec: <%s>\n" + "\tchannel map: <%s>\n" + "\tresample method: %s\n", + o->index, + o->name, + o->driver, + state_table[o->state], + o->source->index, o->source->name, + pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec), + pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map), + pa_resample_method_to_string(pa_source_output_get_resample_method(o))); + if (o->owner) + pa_strbuf_printf(s, "\towner module: <%u>\n", o->owner->index); + if (o->client) + pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", o->client->index, o->client->name); + } + + return pa_strbuf_tostring_free(s); +} + +char *pa_sink_input_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_sink_input *i; + uint32_t idx = PA_IDXSET_INVALID; + static const char* const state_table[] = { + "RUNNING", + "CORKED", + "DISCONNECTED" + }; + + assert(c); + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs)); + + for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) { + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + + assert(i->sink); + + pa_strbuf_printf( + s, + " index: %u\n" + "\tname: <%s>\n" + "\tdriver: <%s>\n" + "\tstate: %s\n" + "\tsink: <%u> '%s'\n" + "\tvolume: <%s>\n" + "\tlatency: <%0.0f usec>\n" + "\tsample spec: <%s>\n" + "\tchannel map: <%s>\n" + "\tresample method: %s\n", + i->index, + i->name, + i->driver, + state_table[i->state], + i->sink->index, i->sink->name, + pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)), + (double) pa_sink_input_get_latency(i), + pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec), + pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), + pa_resample_method_to_string(pa_sink_input_get_resample_method(i))); + + if (i->owner) + pa_strbuf_printf(s, "\towner module: <%u>\n", i->owner->index); + if (i->client) + pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", i->client->index, i->client->name); + } + + return pa_strbuf_tostring_free(s); +} + +char *pa_scache_list_to_string(pa_core *c) { + pa_strbuf *s; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u cache entries available.\n", c->scache ? pa_idxset_size(c->scache) : 0); + + if (c->scache) { + pa_scache_entry *e; + uint32_t idx = PA_IDXSET_INVALID; + + for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) { + double l = 0; + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + + if (e->memchunk.memblock) { + pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec); + pa_channel_map_snprint(cm, sizeof(cm), &e->channel_map); + l = (double) e->memchunk.length / pa_bytes_per_second(&e->sample_spec); + } + + pa_strbuf_printf( + s, + " name: <%s>\n" + "\tindex: <%u>\n" + "\tsample spec: <%s>\n" + "\tchannel map: <%s>\n" + "\tlength: <%u>\n" + "\tduration: <%0.1fs>\n" + "\tvolume: <%s>\n" + "\tlazy: %s\n" + "\tfilename: %s\n", + e->name, + e->index, + ss, + cm, + e->memchunk.memblock ? e->memchunk.length : 0, + l, + pa_cvolume_snprint(cv, sizeof(cv), &e->volume), + e->lazy ? "yes" : "no", + e->filename ? e->filename : "n/a"); + } + } + + return pa_strbuf_tostring_free(s); +} + +char *pa_autoload_list_to_string(pa_core *c) { + pa_strbuf *s; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u autoload entries available.\n", c->autoload_hashmap ? pa_hashmap_size(c->autoload_hashmap) : 0); + + if (c->autoload_hashmap) { + pa_autoload_entry *e; + void *state = NULL; + + while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) { + pa_strbuf_printf( + s, " name: <%s>\n\ttype: <%s>\n\tindex: <%u>\n\tmodule_name: <%s>\n\targuments: <%s>\n", + e->name, + e->type == PA_NAMEREG_SOURCE ? "source" : "sink", + e->index, + e->module, + e->argument); + + } + } + + return pa_strbuf_tostring_free(s); +} + +char *pa_full_status_string(pa_core *c) { + pa_strbuf *s; + int i; + + s = pa_strbuf_new(); + + for (i = 0; i < 8; i++) { + char *t = NULL; + + switch (i) { + case 0: + t = pa_sink_list_to_string(c); + break; + case 1: + t = pa_source_list_to_string(c); + break; + case 2: + t = pa_sink_input_list_to_string(c); + break; + case 3: + t = pa_source_output_list_to_string(c); + break; + case 4: + t = pa_client_list_to_string(c); + break; + case 5: + t = pa_module_list_to_string(c); + break; + case 6: + t = pa_scache_list_to_string(c); + break; + case 7: + t = pa_autoload_list_to_string(c); + break; + } + + pa_strbuf_puts(s, t); + pa_xfree(t); + } + + return pa_strbuf_tostring_free(s); +} diff --git a/src/polypcore/cli-text.h b/src/polypcore/cli-text.h new file mode 100644 index 00000000..7a1a0361 --- /dev/null +++ b/src/polypcore/cli-text.h @@ -0,0 +1,42 @@ +#ifndef fooclitexthfoo +#define fooclitexthfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "core.h" + +/* Some functions to generate pretty formatted listings of + * entities. The returned strings have to be freed manually. */ + +char *pa_sink_input_list_to_string(pa_core *c); +char *pa_source_output_list_to_string(pa_core *c); +char *pa_sink_list_to_string(pa_core *core); +char *pa_source_list_to_string(pa_core *c); +char *pa_client_list_to_string(pa_core *c); +char *pa_module_list_to_string(pa_core *c); +char *pa_scache_list_to_string(pa_core *c); +char *pa_autoload_list_to_string(pa_core *c); + +char *pa_full_status_string(pa_core *c); + +#endif + diff --git a/src/polypcore/cli.c b/src/polypcore/cli.c new file mode 100644 index 00000000..bc0c285d --- /dev/null +++ b/src/polypcore/cli.c @@ -0,0 +1,147 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "ioline.h" +#include "cli.h" +#include "module.h" +#include "sink.h" +#include "source.h" +#include "client.h" +#include "sink-input.h" +#include "source-output.h" +#include "tokenizer.h" +#include "strbuf.h" +#include "namereg.h" +#include "cli-text.h" +#include "cli-command.h" +#include "xmalloc.h" +#include "log.h" + +#define PROMPT ">>> " + +struct pa_cli { + pa_core *core; + pa_ioline *line; + + void (*eof_callback)(pa_cli *c, void *userdata); + void *userdata; + + pa_client *client; + + int fail, kill_requested, defer_kill; +}; + +static void line_callback(pa_ioline *line, const char *s, void *userdata); +static void client_kill(pa_client *c); + +pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) { + char cname[256]; + pa_cli *c; + assert(io); + + c = pa_xmalloc(sizeof(pa_cli)); + c->core = core; + c->line = pa_ioline_new(io); + assert(c->line); + + c->userdata = NULL; + c->eof_callback = NULL; + + pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); + c->client = pa_client_new(core, __FILE__, cname); + assert(c->client); + c->client->kill = client_kill; + c->client->userdata = c; + c->client->owner = m; + + pa_ioline_set_callback(c->line, line_callback, c); + pa_ioline_puts(c->line, "Welcome to polypaudio! Use \"help\" for usage information.\n"PROMPT); + + c->fail = c->kill_requested = c->defer_kill = 0; + + return c; +} + +void pa_cli_free(pa_cli *c) { + assert(c); + pa_ioline_close(c->line); + pa_ioline_unref(c->line); + pa_client_free(c->client); + pa_xfree(c); +} + +static void client_kill(pa_client *client) { + pa_cli *c; + assert(client && client->userdata); + c = client->userdata; + + pa_log_debug(__FILE__": CLI client killed.\n"); + if (c->defer_kill) + c->kill_requested = 1; + else { + if (c->eof_callback) + c->eof_callback(c, c->userdata); + } +} + +static void line_callback(pa_ioline *line, const char *s, void *userdata) { + pa_strbuf *buf; + pa_cli *c = userdata; + char *p; + assert(line && c); + + if (!s) { + pa_log_debug(__FILE__": CLI got EOF from user.\n"); + if (c->eof_callback) + c->eof_callback(c, c->userdata); + + return; + } + + buf = pa_strbuf_new(); + assert(buf); + c->defer_kill++; + pa_cli_command_execute_line(c->core, s, buf, &c->fail); + c->defer_kill--; + pa_ioline_puts(line, p = pa_strbuf_tostring_free(buf)); + pa_xfree(p); + + if (c->kill_requested) { + if (c->eof_callback) + c->eof_callback(c, c->userdata); + } else + pa_ioline_puts(line, PROMPT); +} + +void pa_cli_set_eof_callback(pa_cli *c, void (*cb)(pa_cli*c, void *userdata), void *userdata) { + assert(c); + c->eof_callback = cb; + c->userdata = userdata; +} diff --git a/src/polypcore/cli.h b/src/polypcore/cli.h new file mode 100644 index 00000000..03f31c22 --- /dev/null +++ b/src/polypcore/cli.h @@ -0,0 +1,38 @@ +#ifndef fooclihfoo +#define fooclihfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "iochannel.h" +#include "core.h" +#include "module.h" + +typedef struct pa_cli pa_cli; + +/* Create a new command line session on the specified io channel owned by the specified module */ +pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m); +void pa_cli_free(pa_cli *cli); + +/* Set a callback function that is called whenever the command line session is terminated */ +void pa_cli_set_eof_callback(pa_cli *cli, void (*cb)(pa_cli*c, void *userdata), void *userdata); + +#endif diff --git a/src/polypcore/client.c b/src/polypcore/client.c new file mode 100644 index 00000000..3c2084bf --- /dev/null +++ b/src/polypcore/client.c @@ -0,0 +1,91 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "client.h" +#include "xmalloc.h" +#include "subscribe.h" +#include "log.h" + +pa_client *pa_client_new(pa_core *core, const char *name, const char *driver) { + pa_client *c; + int r; + assert(core); + + c = pa_xmalloc(sizeof(pa_client)); + c->name = pa_xstrdup(name); + c->driver = pa_xstrdup(driver); + c->owner = NULL; + c->core = core; + + c->kill = NULL; + c->userdata = NULL; + + r = pa_idxset_put(core->clients, c, &c->index); + assert(c->index != PA_IDXSET_INVALID && r >= 0); + + pa_log_info(__FILE__": created %u \"%s\"\n", c->index, c->name); + pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index); + + pa_core_check_quit(core); + + return c; +} + +void pa_client_free(pa_client *c) { + assert(c && c->core); + + pa_idxset_remove_by_data(c->core->clients, c, NULL); + + pa_core_check_quit(c->core); + + pa_log_info(__FILE__": freed %u \"%s\"\n", c->index, c->name); + pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index); + pa_xfree(c->name); + pa_xfree(c->driver); + pa_xfree(c); +} + +void pa_client_kill(pa_client *c) { + assert(c); + if (!c->kill) { + pa_log_warn(__FILE__": kill() operation not implemented for client %u\n", c->index); + return; + } + + c->kill(c); +} + +void pa_client_set_name(pa_client *c, const char *name) { + assert(c); + pa_xfree(c->name); + c->name = pa_xstrdup(name); + + pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index); +} diff --git a/src/polypcore/client.h b/src/polypcore/client.h new file mode 100644 index 00000000..92430338 --- /dev/null +++ b/src/polypcore/client.h @@ -0,0 +1,57 @@ +#ifndef fooclienthfoo +#define fooclienthfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "core.h" +#include "module.h" + +/* Every connection to the server should have a pa_client + * attached. That way the user may generate a listing of all connected + * clients easily and kill them if he wants.*/ + +typedef struct pa_client pa_client; + +struct pa_client { + uint32_t index; + + pa_module *owner; + char *name, *driver; + pa_core *core; + + void (*kill)(pa_client *c); + void *userdata; +}; + +pa_client *pa_client_new(pa_core *c, const char *name, const char *driver); + +/* This function should be called only by the code that created the client */ +void pa_client_free(pa_client *c); + +/* Code that didn't create the client should call this function to + * request destruction of the client */ +void pa_client_kill(pa_client *c); + +/* Rename the client */ +void pa_client_set_name(pa_client *c, const char *name); + +#endif diff --git a/src/polypcore/conf-parser.c b/src/polypcore/conf-parser.c new file mode 100644 index 00000000..507f2bf1 --- /dev/null +++ b/src/polypcore/conf-parser.c @@ -0,0 +1,176 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "conf-parser.h" +#include "log.h" +#include "util.h" +#include "xmalloc.h" + +#define WHITESPACE " \t\n" +#define COMMENTS "#;\n" + +/* Run the user supplied parser for an assignment */ +static int next_assignment(const char *filename, unsigned line, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) { + assert(filename && t && lvalue && rvalue); + + for (; t->parse; t++) + if (!strcmp(lvalue, t->lvalue)) + return t->parse(filename, line, lvalue, rvalue, t->data, userdata); + + pa_log(__FILE__": [%s:%u] Unknown lvalue '%s'.\n", filename, line, lvalue); + + return -1; +} + +/* Returns non-zero when c is contained in s */ +static int in_string(char c, const char *s) { + assert(s); + + for (; *s; s++) + if (*s == c) + return 1; + + return 0; +} + +/* Remove all whitepsapce from the beginning and the end of *s. *s may + * be modified. */ +static char *strip(char *s) { + char *b = s+strspn(s, WHITESPACE); + char *e, *l = NULL; + + for (e = b; *e; e++) + if (!in_string(*e, WHITESPACE)) + l = e; + + if (l) + *(l+1) = 0; + + return b; +} + +/* Parse a variable assignment line */ +static int parse_line(const char *filename, unsigned line, const pa_config_item *t, char *l, void *userdata) { + char *e, *c, *b = l+strspn(l, WHITESPACE); + + if ((c = strpbrk(b, COMMENTS))) + *c = 0; + + if (!*b) + return 0; + + if (!(e = strchr(b, '='))) { + pa_log(__FILE__": [%s:%u] Missing '='.\n", filename, line); + return -1; + } + + *e = 0; + e++; + + return next_assignment(filename, line, t, strip(b), strip(e), userdata); +} + +/* Go through the file and parse each line */ +int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata) { + int r = -1; + unsigned line = 0; + int do_close = !f; + assert(filename && t); + + if (!f && !(f = fopen(filename, "r"))) { + if (errno == ENOENT) { + r = 0; + goto finish; + } + + pa_log(__FILE__": WARNING: failed to open configuration file '%s': %s\n", filename, strerror(errno)); + goto finish; + } + + while (!feof(f)) { + char l[256]; + if (!fgets(l, sizeof(l), f)) { + if (feof(f)) + break; + + pa_log(__FILE__": WARNING: failed to read configuration file '%s': %s\n", filename, strerror(errno)); + goto finish; + } + + if (parse_line(filename, ++line, t, l, userdata) < 0) + goto finish; + } + + r = 0; + +finish: + + if (do_close && f) + fclose(f); + + return r; +} + +int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { + int *i = data; + int32_t k; + assert(filename && lvalue && rvalue && data); + + if (pa_atoi(rvalue, &k) < 0) { + pa_log(__FILE__": [%s:%u] Failed to parse numeric value: %s\n", filename, line, rvalue); + return -1; + } + + *i = (int) k; + return 0; +} + +int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { + int *b = data, k; + assert(filename && lvalue && rvalue && data); + + if ((k = pa_parse_boolean(rvalue)) < 0) { + pa_log(__FILE__": [%s:%u] Failed to parse boolean value: %s\n", filename, line, rvalue); + return -1; + } + + *b = k; + + return 0; +} + +int pa_config_parse_string(const char *filename, PA_GCC_UNUSED unsigned line, const char *lvalue, const char *rvalue, void *data, PA_GCC_UNUSED void *userdata) { + char **s = data; + assert(filename && lvalue && rvalue && data); + + pa_xfree(*s); + *s = *rvalue ? pa_xstrdup(rvalue) : NULL; + return 0; +} diff --git a/src/polypcore/conf-parser.h b/src/polypcore/conf-parser.h new file mode 100644 index 00000000..2dca3bce --- /dev/null +++ b/src/polypcore/conf-parser.h @@ -0,0 +1,47 @@ +#ifndef fooconfparserhfoo +#define fooconfparserhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +/* An abstract parser for simple, line based, shallow configuration + * files consisting of variable assignments only. */ + +/* Wraps info for parsing a specific configuration variable */ +typedef struct pa_config_item { + const char *lvalue; /* name of the variable */ + int (*parse)(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); /* Function that is called to parse the variable's value */ + void *data; /* Where to store the variable's data */ +} pa_config_item; + +/* The configuration file parsing routine. Expects a table of + * pa_config_items in *t that is terminated by an item where lvalue is + * NULL */ +int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata); + +/* Generic parsers for integers, booleans and strings */ +int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); +int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); +int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); + +#endif diff --git a/src/polypcore/core.c b/src/polypcore/core.c new file mode 100644 index 00000000..678e8212 --- /dev/null +++ b/src/polypcore/core.c @@ -0,0 +1,157 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "core.h" +#include "module.h" +#include "sink.h" +#include "source.h" +#include "namereg.h" +#include "util.h" +#include "scache.h" +#include "autoload.h" +#include "xmalloc.h" +#include "subscribe.h" +#include "props.h" +#include "random.h" + +pa_core* pa_core_new(pa_mainloop_api *m) { + pa_core* c; + c = pa_xmalloc(sizeof(pa_core)); + + c->mainloop = m; + c->clients = pa_idxset_new(NULL, NULL); + c->sinks = pa_idxset_new(NULL, NULL); + c->sources = pa_idxset_new(NULL, NULL); + c->source_outputs = pa_idxset_new(NULL, NULL); + c->sink_inputs = pa_idxset_new(NULL, NULL); + + c->default_source_name = c->default_sink_name = NULL; + + c->modules = NULL; + c->namereg = NULL; + c->scache = NULL; + c->autoload_idxset = NULL; + c->autoload_hashmap = NULL; + c->running_as_daemon = 0; + + c->default_sample_spec.format = PA_SAMPLE_S16NE; + c->default_sample_spec.rate = 44100; + c->default_sample_spec.channels = 2; + + c->module_auto_unload_event = NULL; + c->module_defer_unload_event = NULL; + c->scache_auto_unload_event = NULL; + + c->subscription_defer_event = NULL; + c->subscription_event_queue = NULL; + c->subscriptions = NULL; + + c->memblock_stat = pa_memblock_stat_new(); + + c->disallow_module_loading = 0; + + c->quit_event = NULL; + + c->exit_idle_time = -1; + c->module_idle_time = 20; + c->scache_idle_time = 20; + + c->resample_method = PA_RESAMPLER_SRC_SINC_FASTEST; + + pa_property_init(c); + + pa_random(&c->cookie, sizeof(c->cookie)); + +#ifdef SIGPIPE + pa_check_signal_is_blocked(SIGPIPE); +#endif + return c; +} + +void pa_core_free(pa_core *c) { + assert(c); + + pa_module_unload_all(c); + assert(!c->modules); + + assert(pa_idxset_isempty(c->clients)); + pa_idxset_free(c->clients, NULL, NULL); + + assert(pa_idxset_isempty(c->sinks)); + pa_idxset_free(c->sinks, NULL, NULL); + + assert(pa_idxset_isempty(c->sources)); + pa_idxset_free(c->sources, NULL, NULL); + + assert(pa_idxset_isempty(c->source_outputs)); + pa_idxset_free(c->source_outputs, NULL, NULL); + + assert(pa_idxset_isempty(c->sink_inputs)); + pa_idxset_free(c->sink_inputs, NULL, NULL); + + pa_scache_free(c); + pa_namereg_free(c); + pa_autoload_free(c); + pa_subscription_free_all(c); + + if (c->quit_event) + c->mainloop->time_free(c->quit_event); + + pa_xfree(c->default_source_name); + pa_xfree(c->default_sink_name); + + pa_memblock_stat_unref(c->memblock_stat); + + pa_property_cleanup(c); + + pa_xfree(c); +} + +static void quit_callback(pa_mainloop_api*m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { + pa_core *c = userdata; + assert(c->quit_event = e); + + m->quit(m, 0); +} + +void pa_core_check_quit(pa_core *c) { + assert(c); + + if (!c->quit_event && c->exit_idle_time >= 0 && pa_idxset_size(c->clients) == 0) { + struct timeval tv; + pa_gettimeofday(&tv); + tv.tv_sec+= c->exit_idle_time; + c->quit_event = c->mainloop->time_new(c->mainloop, &tv, quit_callback, c); + } else if (c->quit_event && pa_idxset_size(c->clients) > 0) { + c->mainloop->time_free(c->quit_event); + c->quit_event = NULL; + } +} + diff --git a/src/polypcore/core.h b/src/polypcore/core.h new file mode 100644 index 00000000..f7a90169 --- /dev/null +++ b/src/polypcore/core.h @@ -0,0 +1,82 @@ +#ifndef foocorehfoo +#define foocorehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct pa_core pa_core; + +#include "idxset.h" +#include "hashmap.h" +#include +#include +#include "memblock.h" +#include "resampler.h" +#include "queue.h" +#include "subscribe.h" + +/* The core structure of polypaudio. Every polypaudio daemon contains + * exactly one of these. It is used for storing kind of global + * variables for the daemon. */ + +struct pa_core { + /* A random value which may be used to identify this instance of + * polypaudio. Not cryptographically secure in any way. */ + uint32_t cookie; + + pa_mainloop_api *mainloop; + + /* idxset of all kinds of entities */ + pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *autoload_idxset; + + /* Some hashmaps for all sorts of entities */ + pa_hashmap *namereg, *autoload_hashmap, *properties; + + /* The name of the default sink/source */ + char *default_source_name, *default_sink_name; + + pa_sample_spec default_sample_spec; + pa_time_event *module_auto_unload_event; + pa_defer_event *module_defer_unload_event; + + pa_defer_event *subscription_defer_event; + pa_queue *subscription_event_queue; + pa_subscription *subscriptions; + + pa_memblock_stat *memblock_stat; + + int disallow_module_loading, running_as_daemon; + int exit_idle_time, module_idle_time, scache_idle_time; + + pa_time_event *quit_event; + + pa_time_event *scache_auto_unload_event; + + pa_resample_method_t resample_method; +}; + +pa_core* pa_core_new(pa_mainloop_api *m); +void pa_core_free(pa_core*c); + +/* Check whether noone is connected to this core */ +void pa_core_check_quit(pa_core *c); + +#endif diff --git a/src/polypcore/dllmain.c b/src/polypcore/dllmain.c new file mode 100644 index 00000000..d1d120ab --- /dev/null +++ b/src/polypcore/dllmain.c @@ -0,0 +1,46 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef OS_IS_WIN32 + +#include +#include +#include + +#include + +extern pa_set_root(HANDLE handle); + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { + if (fdwReason != DLL_PROCESS_ATTACH) + return TRUE; + + if (!pa_set_root(hinstDLL)) + return FALSE; + + return TRUE; +} + +#endif /* OS_IS_WIN32 */ diff --git a/src/polypcore/dynarray.c b/src/polypcore/dynarray.c new file mode 100644 index 00000000..435fd768 --- /dev/null +++ b/src/polypcore/dynarray.c @@ -0,0 +1,102 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "dynarray.h" +#include "xmalloc.h" + +/* If the array becomes to small, increase its size by 100 entries */ +#define INCREASE_BY 100 + +struct pa_dynarray { + void **data; + unsigned n_allocated, n_entries; +}; + +pa_dynarray* pa_dynarray_new(void) { + pa_dynarray *a; + a = pa_xnew(pa_dynarray, 1); + a->data = NULL; + a->n_entries = 0; + a->n_allocated = 0; + return a; +} + +void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata) { + unsigned i; + assert(a); + + if (func) + for (i = 0; i < a->n_entries; i++) + if (a->data[i]) + func(a->data[i], userdata); + + pa_xfree(a->data); + pa_xfree(a); +} + +void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p) { + assert(a); + + if (i >= a->n_allocated) { + unsigned n; + + if (!p) + return; + + n = i+INCREASE_BY; + a->data = pa_xrealloc(a->data, sizeof(void*)*n); + memset(a->data+a->n_allocated, 0, sizeof(void*)*(n-a->n_allocated)); + a->n_allocated = n; + } + + a->data[i] = p; + + if (i >= a->n_entries) + a->n_entries = i+1; +} + +unsigned pa_dynarray_append(pa_dynarray*a, void *p) { + unsigned i = a->n_entries; + pa_dynarray_put(a, i, p); + return i; +} + +void *pa_dynarray_get(pa_dynarray*a, unsigned i) { + assert(a); + if (i >= a->n_allocated) + return NULL; + + assert(a->data); + return a->data[i]; +} + +unsigned pa_dynarray_size(pa_dynarray*a) { + assert(a); + return a->n_entries; +} diff --git a/src/polypcore/dynarray.h b/src/polypcore/dynarray.h new file mode 100644 index 00000000..9b1601ba --- /dev/null +++ b/src/polypcore/dynarray.h @@ -0,0 +1,49 @@ +#ifndef foodynarrayhfoo +#define foodynarrayhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct pa_dynarray pa_dynarray; + +/* Implementation of a simple dynamically sized array. The array + * expands if required, but doesn't shrink if possible. Memory + * management of the array's entries is the user's job. */ + +pa_dynarray* pa_dynarray_new(void); + +/* Free the array calling the specified function for every entry in + * the array. The function may be NULL. */ +void pa_dynarray_free(pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata); + +/* Store p at position i in the array */ +void pa_dynarray_put(pa_dynarray*a, unsigned i, void *p); + +/* Store p a the first free position in the array. Returns the index + * of that entry. If entries are removed from the array their position + * are not filled any more by this function. */ +unsigned pa_dynarray_append(pa_dynarray*a, void *p); + +void *pa_dynarray_get(pa_dynarray*a, unsigned i); + +unsigned pa_dynarray_size(pa_dynarray*a); + +#endif diff --git a/src/polypcore/endianmacros.h b/src/polypcore/endianmacros.h new file mode 100644 index 00000000..3ab1826a --- /dev/null +++ b/src/polypcore/endianmacros.h @@ -0,0 +1,77 @@ +#ifndef fooendianmacroshfoo +#define fooendianmacroshfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define INT16_SWAP(x) ( (int16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) ) +#define UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) ) +#define INT32_SWAP(x) ( (int32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) ) +#define UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) ) + +#define MAYBE_INT32_SWAP(c,x) ((c) ? INT32_SWAP(x) : x) +#define MAYBE_UINT32_SWAP(c,x) ((c) ? UINT32_SWAP(x) : x) + +#ifdef WORDS_BIGENDIAN + #define INT16_FROM_LE(x) INT16_SWAP(x) + #define INT16_FROM_BE(x) ((int16_t)(x)) + + #define INT16_TO_LE(x) INT16_SWAP(x) + #define INT16_TO_BE(x) ((int16_t)(x)) + + #define UINT16_FROM_LE(x) UINT16_SWAP(x) + #define UINT16_FROM_BE(x) ((uint16_t)(x)) + + #define INT32_FROM_LE(x) INT32_SWAP(x) + #define INT32_FROM_BE(x) ((int32_t)(x)) + + #define UINT32_FROM_LE(x) UINT32_SWAP(x) + #define UINT32_FROM_BE(x) ((uint32_t)(x)) + + #define UINT32_TO_LE(x) UINT32_SWAP(x) + #define UINT32_TO_BE(x) ((uint32_t)(x)) +#else + #define INT16_FROM_LE(x) ((int16_t)(x)) + #define INT16_FROM_BE(x) INT16_SWAP(x) + + #define INT16_TO_LE(x) ((int16_t)(x)) + #define INT16_TO_BE(x) INT16_SWAP(x) + + #define UINT16_FROM_LE(x) ((uint16_t)(x)) + #define UINT16_FROM_BE(x) UINT16_SWAP(x) + + #define INT32_FROM_LE(x) ((int32_t)(x)) + #define INT32_FROM_BE(x) INT32_SWAP(x) + + #define UINT32_FROM_LE(x) ((uint32_t)(x)) + #define UINT32_FROM_BE(x) UINT32_SWAP(x) + + #define UINT32_TO_LE(x) ((uint32_t)(x)) + #define UINT32_TO_BE(x) UINT32_SWAP(x) +#endif + +#endif diff --git a/src/polypcore/esound.h b/src/polypcore/esound.h new file mode 100644 index 00000000..9c507ef9 --- /dev/null +++ b/src/polypcore/esound.h @@ -0,0 +1,209 @@ +#ifndef fooesoundhfoo +#define fooesoundhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* Most of the following is blatantly stolen from esound. */ + + +/* path and name of the default EsounD domain socket */ +#define ESD_UNIX_SOCKET_DIR "/tmp/.esd" +#define ESD_UNIX_SOCKET_NAME "/tmp/.esd/socket" + +/* length of the audio buffer size */ +#define ESD_BUF_SIZE (4 * 1024) +/* maximum size we can write(). Otherwise we might overflow */ +#define ESD_MAX_WRITE_SIZE (21 * 4096) + +/* length of the authorization key, octets */ +#define ESD_KEY_LEN (16) + +/* default port for the EsounD server */ +#define ESD_DEFAULT_PORT (16001) + +/* default sample rate for the EsounD server */ +#define ESD_DEFAULT_RATE (44100) + +/* maximum length of a stream/sample name */ +#define ESD_NAME_MAX (128) + +/* a magic number to identify the relative endianness of a client */ +#define ESD_ENDIAN_KEY ((uint32_t) (('E' << 24) + ('N' << 16) + ('D' << 8) + ('N'))) + +#define ESD_VOLUME_BASE (256) + + +/*************************************/ +/* what can we do to/with the EsounD */ +enum esd_proto { + ESD_PROTO_CONNECT, /* implied on inital client connection */ + + /* pseudo "security" functionality */ + ESD_PROTO_LOCK, /* disable "foreign" client connections */ + ESD_PROTO_UNLOCK, /* enable "foreign" client connections */ + + /* stream functionality: play, record, monitor */ + ESD_PROTO_STREAM_PLAY, /* play all following data as a stream */ + ESD_PROTO_STREAM_REC, /* record data from card as a stream */ + ESD_PROTO_STREAM_MON, /* send mixed buffer output as a stream */ + + /* sample functionality: cache, free, play, loop, EOL, kill */ + ESD_PROTO_SAMPLE_CACHE, /* cache a sample in the server */ + ESD_PROTO_SAMPLE_FREE, /* release a sample in the server */ + ESD_PROTO_SAMPLE_PLAY, /* play a cached sample */ + ESD_PROTO_SAMPLE_LOOP, /* loop a cached sample, til eoloop */ + ESD_PROTO_SAMPLE_STOP, /* stop a looping sample when done */ + ESD_PROTO_SAMPLE_KILL, /* stop the looping sample immed. */ + + /* free and reclaim /dev/dsp functionality */ + ESD_PROTO_STANDBY, /* release /dev/dsp and ignore all data */ + ESD_PROTO_RESUME, /* reclaim /dev/dsp and play sounds again */ + + /* TODO: move these to a more logical place. NOTE: will break the protocol */ + ESD_PROTO_SAMPLE_GETID, /* get the ID for an already-cached sample */ + ESD_PROTO_STREAM_FILT, /* filter mixed buffer output as a stream */ + + /* esd remote management */ + ESD_PROTO_SERVER_INFO, /* get server info (ver, sample rate, format) */ + ESD_PROTO_ALL_INFO, /* get all info (server info, players, samples) */ + ESD_PROTO_SUBSCRIBE, /* track new and removed players and samples */ + ESD_PROTO_UNSUBSCRIBE, /* stop tracking updates */ + + /* esd remote control */ + ESD_PROTO_STREAM_PAN, /* set stream panning */ + ESD_PROTO_SAMPLE_PAN, /* set default sample panning */ + + /* esd status */ + ESD_PROTO_STANDBY_MODE, /* see if server is in standby, autostandby, etc */ + + /* esd latency */ + ESD_PROTO_LATENCY, /* retrieve latency between write()'s and output */ + + ESD_PROTO_MAX /* for bounds checking */ +}; + +/******************/ +/* The EsounD api */ + +/* the properties of a sound buffer are logically or'd */ + +/* bits of stream/sample data */ +#define ESD_MASK_BITS ( 0x000F ) +#define ESD_BITS8 ( 0x0000 ) +#define ESD_BITS16 ( 0x0001 ) + +/* how many interleaved channels of data */ +#define ESD_MASK_CHAN ( 0x00F0 ) +#define ESD_MONO ( 0x0010 ) +#define ESD_STEREO ( 0x0020 ) + +/* whether it's a stream or a sample */ +#define ESD_MASK_MODE ( 0x0F00 ) +#define ESD_STREAM ( 0x0000 ) +#define ESD_SAMPLE ( 0x0100 ) +#define ESD_ADPCM ( 0x0200 ) /* TODO: anyone up for this? =P */ + +/* the function of the stream/sample, and common functions */ +#define ESD_MASK_FUNC ( 0xF000 ) +#define ESD_PLAY ( 0x1000 ) +/* functions for streams only */ +#define ESD_MONITOR ( 0x0000 ) +#define ESD_RECORD ( 0x2000 ) +/* functions for samples only */ +#define ESD_STOP ( 0x0000 ) +#define ESD_LOOP ( 0x2000 ) + +typedef int esd_format_t; +typedef int esd_proto_t; + +/*******************************************************************/ +/* esdmgr.c - functions to implement a "sound manager" for esd */ + +/* structures to retrieve information about streams/samples from the server */ +typedef struct esd_server_info { + + int version; /* server version encoded as an int */ + esd_format_t format; /* magic int with the format info */ + int rate; /* sample rate */ + +} esd_server_info_t; + +typedef struct esd_player_info { + + struct esd_player_info *next; /* point to next entry in list */ + esd_server_info_t *server; /* the server that contains this stream */ + + int source_id; /* either a stream fd or sample id */ + char name[ ESD_NAME_MAX ]; /* name of stream for remote control */ + int rate; /* sample rate */ + int left_vol_scale; /* volume scaling */ + int right_vol_scale; + + esd_format_t format; /* magic int with the format info */ + +} esd_player_info_t; + +typedef struct esd_sample_info { + + struct esd_sample_info *next; /* point to next entry in list */ + esd_server_info_t *server; /* the server that contains this sample */ + + int sample_id; /* either a stream fd or sample id */ + char name[ ESD_NAME_MAX ]; /* name of stream for remote control */ + int rate; /* sample rate */ + int left_vol_scale; /* volume scaling */ + int right_vol_scale; + + esd_format_t format; /* magic int with the format info */ + int length; /* total buffer length */ + +} esd_sample_info_t; + +typedef struct esd_info { + + esd_server_info_t *server; + esd_player_info_t *player_list; + esd_sample_info_t *sample_list; + +} esd_info_t; + +enum esd_standby_mode { + ESM_ERROR, ESM_ON_STANDBY, ESM_ON_AUTOSTANDBY, ESM_RUNNING +}; +typedef int esd_standby_mode_t; + +enum esd_client_state { + ESD_STREAMING_DATA, /* data from here on is streamed data */ + ESD_CACHING_SAMPLE, /* midway through caching a sample */ + ESD_NEEDS_REQDATA, /* more data needed to complere request */ + ESD_NEXT_REQUEST, /* proceed to next request */ + ESD_CLIENT_STATE_MAX /* place holder */ +}; +typedef int esd_client_state_t; + +/* the endian key is transferred in binary, if it's read into int, */ +/* and matches ESD_ENDIAN_KEY (ENDN), then the endianness of the */ +/* server and the client match; if it's SWAP_ENDIAN_KEY, swap data */ +#define ESD_SWAP_ENDIAN_KEY (UINT32_SWAP(ESD_ENDIAN_KEY)) + + +#endif diff --git a/src/polypcore/g711.c b/src/polypcore/g711.c new file mode 100644 index 00000000..55a82396 --- /dev/null +++ b/src/polypcore/g711.c @@ -0,0 +1,2531 @@ +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * g711.c + * + * u-law, A-law and linear PCM conversions. + */ + +/* + * December 30, 1994: + * Functions linear2alaw, linear2ulaw have been updated to correctly + * convert unquantized 16 bit values. + * Tables for direct u- to A-law and A- to u-law conversions have been + * corrected. + * Borge Lindberg, Center for PersonKommunikation, Aalborg University. + * bli@cpk.auc.dk + * + */ + +#include "g711.h" + +#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ +#define QUANT_MASK (0xf) /* Quantization field mask. */ +#define NSEGS (8) /* Number of A-law segments. */ +#define SEG_SHIFT (4) /* Left shift for segment number. */ +#define SEG_MASK (0x70) /* Segment field mask. */ + +#if !defined(FAST_ALAW_CONVERSION) || !defined(FAST_ULAW_CONVERSION) +static int16_t seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF, + 0x1FF, 0x3FF, 0x7FF, 0xFFF}; +static int16_t seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF, + 0x3FF, 0x7FF, 0xFFF, 0x1FFF}; + +static int16_t search( + int16_t val, + int16_t *table, + int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (val <= *table++) + return (i); + } + return (size); +} +#endif /* !FAST_*_CONVERSION */ + +#ifndef FAST_ALAW_CONVERSION +/* + * linear2alaw() accepts an 13-bit signed integer and encodes it as A-law data + * stored in a unsigned char. This function should only be called with + * the data shifted such that it only contains information in the lower + * 13-bits. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +unsigned char st_13linear2alaw( + int16_t pcm_val) /* 2's complement (13-bit range) */ +{ + int16_t mask; + short seg; + unsigned char aval; + + /* Have calling software do it since its already doing a shift + * from 32-bits down to 16-bits. + */ + /* pcm_val = pcm_val >> 3; */ + + /* A-law using even bit inversion */ + if (pcm_val >= 0) { + mask = 0xD5; /* sign (7th) bit = 1 */ + } else { + mask = 0x55; /* sign bit = 0 */ + pcm_val = -pcm_val - 1; + } + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm_val, seg_aend, 8); + + /* Combine the sign, segment, and quantization bits. */ + + if (seg >= 8) /* out of range, return maximum value. */ + return (unsigned char) (0x7F ^ mask); + else { + aval = (unsigned char) seg << SEG_SHIFT; + if (seg < 2) + aval |= (pcm_val >> 1) & QUANT_MASK; + else + aval |= (pcm_val >> seg) & QUANT_MASK; + return (aval ^ mask); + } +} + +/* + * alaw2linear() - Convert an A-law value to 16-bit signed linear PCM + * + */ +int16_t st_alaw2linear16( + unsigned char a_val) +{ + int16_t t; + int16_t seg; + + a_val ^= 0x55; + + t = (a_val & QUANT_MASK) << 4; + seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; + switch (seg) { + case 0: + t += 8; + break; + case 1: + t += 0x108; + break; + default: + t += 0x108; + t <<= seg - 1; + } + return ((a_val & SIGN_BIT) ? t : -t); +} +#endif /* !FAST_ALAW_CONVERSION */ + +#define BIAS (0x84) /* Bias for linear code. */ +#define CLIP 8159 + +#ifndef FAST_ULAW_CONVERSION +/* + * linear2ulaw() accepts a 14-bit signed integer and encodes it as u-law data + * stored in a unsigned char. This function should only be called with + * the data shifted such that it only contains information in the lower + * 14-bits. + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +unsigned char st_14linear2ulaw( + int16_t pcm_val) /* 2's complement (14-bit range) */ +{ + int16_t mask; + int16_t seg; + unsigned char uval; + + /* Have calling software do it since its already doing a shift + * from 32-bits down to 16-bits. + */ + /* pcm_val = pcm_val >> 2; */ + + /* u-law inverts all bits */ + /* Get the sign and the magnitude of the value. */ + if (pcm_val < 0) { + pcm_val = -pcm_val; + mask = 0x7F; + } else { + mask = 0xFF; + } + if ( pcm_val > CLIP ) pcm_val = CLIP; /* clip the magnitude */ + pcm_val += (BIAS >> 2); + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm_val, seg_uend, 8); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + if (seg >= 8) /* out of range, return maximum value. */ + return (unsigned char) (0x7F ^ mask); + else { + uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF); + return (uval ^ mask); + } + +} + +/* + * ulaw2linear() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +int16_t st_ulaw2linear16( + unsigned char u_val) +{ + int16_t t; + + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & QUANT_MASK) << 3) + BIAS; + t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; + + return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); +} +#endif /* !FAST_ULAW_CONVERSION */ + +#ifdef FAST_ALAW_CONVERSION + +int16_t _st_alaw2linear16[256] = { + -5504, -5248, -6016, -5760, -4480, -4224, -4992, + -4736, -7552, -7296, -8064, -7808, -6528, -6272, + -7040, -6784, -2752, -2624, -3008, -2880, -2240, + -2112, -2496, -2368, -3776, -3648, -4032, -3904, + -3264, -3136, -3520, -3392, -22016, -20992, -24064, + -23040, -17920, -16896, -19968, -18944, -30208, -29184, + -32256, -31232, -26112, -25088, -28160, -27136, -11008, + -10496, -12032, -11520, -8960, -8448, -9984, -9472, + -15104, -14592, -16128, -15616, -13056, -12544, -14080, + -13568, -344, -328, -376, -360, -280, -264, + -312, -296, -472, -456, -504, -488, -408, + -392, -440, -424, -88, -72, -120, -104, + -24, -8, -56, -40, -216, -200, -248, + -232, -152, -136, -184, -168, -1376, -1312, + -1504, -1440, -1120, -1056, -1248, -1184, -1888, + -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, + -592, -944, -912, -1008, -976, -816, -784, + -880, -848, 5504, 5248, 6016, 5760, 4480, + 4224, 4992, 4736, 7552, 7296, 8064, 7808, + 6528, 6272, 7040, 6784, 2752, 2624, 3008, + 2880, 2240, 2112, 2496, 2368, 3776, 3648, + 4032, 3904, 3264, 3136, 3520, 3392, 22016, + 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, + 27136, 11008, 10496, 12032, 11520, 8960, 8448, + 9984, 9472, 15104, 14592, 16128, 15616, 13056, + 12544, 14080, 13568, 344, 328, 376, 360, + 280, 264, 312, 296, 472, 456, 504, + 488, 408, 392, 440, 424, 88, 72, + 120, 104, 24, 8, 56, 40, 216, + 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, + 1184, 1888, 1824, 2016, 1952, 1632, 1568, + 1760, 1696, 688, 656, 752, 720, 560, + 528, 624, 592, 944, 912, 1008, 976, + 816, 784, 880, 848 +}; + +uint8_t _st_13linear2alaw[0x2000] = { + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x3a, 0x3a, 0x3a, 0x3a, + 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, + 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, + 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, + 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, + 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, + 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, + 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, + 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, + 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, + 0x37, 0x37, 0x37, 0x37, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, + 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, + 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, + 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, + 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, + 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, + 0x6b, 0x6b, 0x6b, 0x6b, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x6e, 0x6e, 0x6e, 0x6e, + 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, + 0x6d, 0x6d, 0x6d, 0x6d, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, + 0x67, 0x67, 0x67, 0x67, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, + 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x7a, 0x7a, 0x7a, 0x7a, + 0x7b, 0x7b, 0x7b, 0x7b, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79, + 0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7c, 0x7c, 0x7c, 0x7c, + 0x7d, 0x7d, 0x7d, 0x7d, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73, + 0x70, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x71, 0x76, 0x76, 0x76, 0x76, + 0x77, 0x77, 0x77, 0x77, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, + 0x4a, 0x4a, 0x4b, 0x4b, 0x48, 0x48, 0x49, 0x49, 0x4e, 0x4e, 0x4f, 0x4f, + 0x4c, 0x4c, 0x4d, 0x4d, 0x42, 0x42, 0x43, 0x43, 0x40, 0x40, 0x41, 0x41, + 0x46, 0x46, 0x47, 0x47, 0x44, 0x44, 0x45, 0x45, 0x5a, 0x5a, 0x5b, 0x5b, + 0x58, 0x58, 0x59, 0x59, 0x5e, 0x5e, 0x5f, 0x5f, 0x5c, 0x5c, 0x5d, 0x5d, + 0x52, 0x52, 0x53, 0x53, 0x50, 0x50, 0x51, 0x51, 0x56, 0x56, 0x57, 0x57, + 0x54, 0x54, 0x55, 0x55, 0xd5, 0xd5, 0xd4, 0xd4, 0xd7, 0xd7, 0xd6, 0xd6, + 0xd1, 0xd1, 0xd0, 0xd0, 0xd3, 0xd3, 0xd2, 0xd2, 0xdd, 0xdd, 0xdc, 0xdc, + 0xdf, 0xdf, 0xde, 0xde, 0xd9, 0xd9, 0xd8, 0xd8, 0xdb, 0xdb, 0xda, 0xda, + 0xc5, 0xc5, 0xc4, 0xc4, 0xc7, 0xc7, 0xc6, 0xc6, 0xc1, 0xc1, 0xc0, 0xc0, + 0xc3, 0xc3, 0xc2, 0xc2, 0xcd, 0xcd, 0xcc, 0xcc, 0xcf, 0xcf, 0xce, 0xce, + 0xc9, 0xc9, 0xc8, 0xc8, 0xcb, 0xcb, 0xca, 0xca, 0xf5, 0xf5, 0xf5, 0xf5, + 0xf4, 0xf4, 0xf4, 0xf4, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf1, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xf0, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf2, 0xf2, 0xf2, 0xf2, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf8, 0xf8, 0xf8, 0xf8, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe4, 0xe4, 0xe4, 0xe4, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe1, 0xe1, 0xe1, 0xe1, + 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe2, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, + 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xef, 0xef, 0xef, 0xef, + 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, + 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, + 0xe8, 0xe8, 0xe8, 0xe8, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, + 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa +}; + +#endif /* FAST_ALAW_CONVERSION */ + +#ifdef FAST_ULAW_CONVERSION + +int16_t _st_ulaw2linear16[256] = { + -32124, -31100, -30076, -29052, -28028, -27004, -25980, + -24956, -23932, -22908, -21884, -20860, -19836, -18812, + -17788, -16764, -15996, -15484, -14972, -14460, -13948, + -13436, -12924, -12412, -11900, -11388, -10876, -10364, + -9852, -9340, -8828, -8316, -7932, -7676, -7420, + -7164, -6908, -6652, -6396, -6140, -5884, -5628, + -5372, -5116, -4860, -4604, -4348, -4092, -3900, + -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, + -1980, -1884, -1820, -1756, -1692, -1628, -1564, + -1500, -1436, -1372, -1308, -1244, -1180, -1116, + -1052, -988, -924, -876, -844, -812, -780, + -748, -716, -684, -652, -620, -588, -556, + -524, -492, -460, -428, -396, -372, -356, + -340, -324, -308, -292, -276, -260, -244, + -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, + -64, -56, -48, -40, -32, -24, -16, + -8, 0, 32124, 31100, 30076, 29052, 28028, + 27004, 25980, 24956, 23932, 22908, 21884, 20860, + 19836, 18812, 17788, 16764, 15996, 15484, 14972, + 14460, 13948, 13436, 12924, 12412, 11900, 11388, + 10876, 10364, 9852, 9340, 8828, 8316, 7932, + 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, + 4092, 3900, 3772, 3644, 3516, 3388, 3260, + 3132, 3004, 2876, 2748, 2620, 2492, 2364, + 2236, 2108, 1980, 1884, 1820, 1756, 1692, + 1628, 1564, 1500, 1436, 1372, 1308, 1244, + 1180, 1116, 1052, 988, 924, 876, 844, + 812, 780, 748, 716, 684, 652, 620, + 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, + 260, 244, 228, 212, 196, 180, 164, + 148, 132, 120, 112, 104, 96, 88, + 80, 72, 64, 56, 48, 40, 32, + 24, 16, 8, 0 +}; + +uint8_t _st_14linear2ulaw[0x4000] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x34, + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, + 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, + 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, + 0x35, 0x35, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, + 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, + 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, + 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x3a, 0x3a, + 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, + 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, + 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, + 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, + 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, + 0x43, 0x43, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, + 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, + 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, + 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, + 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, + 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, + 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, + 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, + 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, + 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f, + 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, + 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, + 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, + 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, + 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57, + 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5a, 0x5a, + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, + 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, + 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60, + 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x62, 0x63, 0x63, + 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66, + 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, + 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, + 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74, + 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a, + 0x7b, 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0xff, 0xfe, 0xfe, 0xfd, + 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf7, + 0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1, + 0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xed, + 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xea, + 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, + 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, 0xe1, + 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, + 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, + 0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xda, + 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, + 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, + 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd4, + 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, + 0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, 0xce, 0xce, 0xce, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xca, + 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, + 0xca, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, + 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, + 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc7, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, + 0xc7, 0xc7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc4, + 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, + 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1, + 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, + 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80 +}; + +#endif /* FAST_ULAW_CONVERSION */ + +/* The following code was used to generate the lookup tables */ +#if 0 +int main() +{ + int x, y, find2a = 0; + + y = 0; + printf("int16_t _st_alaw2linear16[256] = {\n "); + for (x = 0; x < 256; x++) + { + printf("%8d,", st_alaw2linear16(x)); + y++; + if (y == 7) + { + y = 0; + printf("\n "); + } + } + + printf("\n};\n\nuint8_t _st_13linear2alaw[0x2000] = {\n "); + y = 0; + for (x = 0; x < 0x2000; x++) + { + printf(" 0x%02x,", st_13linear2alaw((-0x1000)+x)); + y++; + if (y == 12) + { + y = 0; + printf("\n "); + } + } + + printf("\n};\n\nint16_t _st_ulaw2linear16[256] = {\n "); + y = 0; + for (x = 0; x < 256; x++) + { + printf("%8d,", st_ulaw2linear16(x)); + y++; + if (y == 7) + { + y = 0; + printf("\n "); + } + } + + printf("\n};\n\nuint8_t _st_14linear2ulaw[0x4000] = {\n "); + y = 0; + for (x = 0; x < 0x4000; x++) + { + printf(" 0x%02x,", st_14linear2ulaw((-0x2000)+x)); + y++; + if (y == 12) + { + y = 0; + printf("\n "); + } + } + printf("\n};\n"); + +} +#endif + +/* The following is not used by SoX but kept for reference */ +#if 0 +/* copy from CCITT G.711 specifications */ +unsigned char _u2a[128] = { /* u- to A-law conversions */ + 1, 1, 2, 2, 3, 3, 4, 4, + 5, 5, 6, 6, 7, 7, 8, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 27, 29, 31, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, + 46, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, +/* corrected: + 81, 82, 83, 84, 85, 86, 87, 88, + should be: */ + 80, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128}; + +unsigned char _a2u[128] = { /* A- to u-law conversions */ + 1, 3, 5, 7, 9, 11, 13, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 32, 33, 33, 34, 34, 35, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 48, 49, 49, + 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 64, + 65, 66, 67, 68, 69, 70, 71, 72, +/* corrected: + 73, 74, 75, 76, 77, 78, 79, 79, + should be: */ + 73, 74, 75, 76, 77, 78, 79, 80, + + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}; + +/* A-law to u-law conversion */ +unsigned char st_alaw2ulaw( + unsigned char aval) +{ + aval &= 0xff; + return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) : + (0x7F ^ _a2u[aval ^ 0x55])); +} + +/* u-law to A-law conversion */ +unsigned char st_ulaw2alaw( + unsigned char uval) +{ + uval &= 0xff; + return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) : + (unsigned char) (0x55 ^ (_u2a[0x7F ^ uval] - 1))); +} +#endif diff --git a/src/polypcore/g711.h b/src/polypcore/g711.h new file mode 100644 index 00000000..97cedf81 --- /dev/null +++ b/src/polypcore/g711.h @@ -0,0 +1,40 @@ +#ifndef foog711hfoo +#define foog711hfoo + +/* g711.h - include for G711 u-law and a-law conversion routines +** +** Copyright (C) 2001 Chris Bagwell +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +/** Copied from sox -- Lennart Poettring*/ + +#include + +#ifdef FAST_ALAW_CONVERSION +extern uint8_t _st_13linear2alaw[0x2000]; +extern int16_t _st_alaw2linear16[256]; +#define st_13linear2alaw(sw) (_st_13linear2alaw[(sw + 0x1000)]) +#define st_alaw2linear16(uc) (_st_alaw2linear16[uc]) +#else +unsigned char st_13linear2alaw(int16_t pcm_val); +int16_t st_alaw2linear16(unsigned char); +#endif + +#ifdef FAST_ULAW_CONVERSION +extern uint8_t _st_14linear2ulaw[0x4000]; +extern int16_t _st_ulaw2linear16[256]; +#define st_14linear2ulaw(sw) (_st_14linear2ulaw[(sw + 0x2000)]) +#define st_ulaw2linear16(uc) (_st_ulaw2linear16[uc]) +#else +unsigned char st_14linear2ulaw(int16_t pcm_val); +int16_t st_ulaw2linear16(unsigned char); +#endif + +#endif diff --git a/src/polypcore/gccmacro.h b/src/polypcore/gccmacro.h new file mode 100644 index 00000000..9e212f2e --- /dev/null +++ b/src/polypcore/gccmacro.h @@ -0,0 +1,53 @@ +#ifndef foogccmacrohfoo +#define foogccmacrohfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef __GNUC__ +#define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b))) +#else +/** If we're in GNU C, use some magic for detecting invalid format strings */ +#define PA_GCC_PRINTF_ATTR(a,b) +#endif + +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define PA_GCC_SENTINEL __attribute__ ((sentinel)) +#else +/** Macro for usage of GCC's sentinel compilation warnings */ +#define PA_GCC_SENTINEL +#endif + +#ifdef __GNUC__ +#define PA_GCC_NORETURN __attribute__((noreturn)) +#else +/** Macro for no-return functions */ +#define PA_GCC_NORETURN +#endif + +#ifdef __GNUC__ +#define PA_GCC_UNUSED __attribute__ ((unused)) +#else +/** Macro for not used parameter */ +#define PA_GCC_UNUSED +#endif + +#endif diff --git a/src/polypcore/hashmap.c b/src/polypcore/hashmap.c new file mode 100644 index 00000000..a37decb8 --- /dev/null +++ b/src/polypcore/hashmap.c @@ -0,0 +1,194 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "hashmap.h" +#include "idxset.h" +#include "xmalloc.h" +#include "log.h" + +#define BUCKETS 1023 + +struct hashmap_entry { + struct hashmap_entry *next, *previous, *bucket_next, *bucket_previous; + unsigned hash; + const void *key; + void *value; +}; + +struct pa_hashmap { + unsigned size; + struct hashmap_entry **data; + struct hashmap_entry *first_entry; + + unsigned n_entries; + unsigned (*hash_func) (const void *p); + int (*compare_func) (const void*a, const void*b); +}; + +pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) { + pa_hashmap *h; + h = pa_xmalloc(sizeof(pa_hashmap)); + h->data = pa_xmalloc0(sizeof(struct hashmap_entry*)*(h->size = BUCKETS)); + h->first_entry = NULL; + h->n_entries = 0; + h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func; + h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func; + return h; +} + +static void remove(pa_hashmap *h, struct hashmap_entry *e) { + assert(e); + + if (e->next) + e->next->previous = e->previous; + if (e->previous) + e->previous->next = e->next; + else + h->first_entry = e->next; + + if (e->bucket_next) + e->bucket_next->bucket_previous = e->bucket_previous; + if (e->bucket_previous) + e->bucket_previous->bucket_next = e->bucket_next; + else { + assert(e->hash < h->size); + h->data[e->hash] = e->bucket_next; + } + + pa_xfree(e); + h->n_entries--; +} + +void pa_hashmap_free(pa_hashmap*h, void (*free_func)(void *p, void *userdata), void *userdata) { + assert(h); + + while (h->first_entry) { + if (free_func) + free_func(h->first_entry->value, userdata); + remove(h, h->first_entry); + } + + pa_xfree(h->data); + pa_xfree(h); +} + +static struct hashmap_entry *get(pa_hashmap *h, unsigned hash, const void *key) { + struct hashmap_entry *e; + assert(h && hash < h->size); + + for (e = h->data[hash]; e; e = e->bucket_next) + if (h->compare_func(e->key, key) == 0) + return e; + + return NULL; +} + +int pa_hashmap_put(pa_hashmap *h, const void *key, void *value) { + struct hashmap_entry *e; + unsigned hash; + assert(h); + + hash = h->hash_func(key) % h->size; + + if ((e = get(h, hash, key))) + return -1; + + e = pa_xmalloc(sizeof(struct hashmap_entry)); + e->hash = hash; + e->key = key; + e->value = value; + + e->previous = NULL; + e->next = h->first_entry; + if (h->first_entry) + h->first_entry->previous = e; + h->first_entry = e; + + e->bucket_previous = NULL; + e->bucket_next = h->data[hash]; + if (h->data[hash]) + h->data[hash]->bucket_previous = e; + h->data[hash] = e; + + h->n_entries ++; + return 0; +} + +void* pa_hashmap_get(pa_hashmap *h, const void *key) { + unsigned hash; + struct hashmap_entry *e; + assert(h && key); + + hash = h->hash_func(key) % h->size; + + if (!(e = get(h, hash, key))) + return NULL; + + return e->value; +} + +void* pa_hashmap_remove(pa_hashmap *h, const void *key) { + struct hashmap_entry *e; + unsigned hash; + void *data; + assert(h && key); + + hash = h->hash_func(key) % h->size; + + if (!(e = get(h, hash, key))) + return NULL; + + data = e->value; + remove(h, e); + return data; +} + +unsigned pa_hashmap_size(pa_hashmap *h) { + return h->n_entries; +} + +void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void **key) { + assert(h && state); + + if (!*state) + *state = h->first_entry; + else + *state = ((struct hashmap_entry*) *state)->next; + + if (!*state) { + if (key) + *key = NULL; + return NULL; + } + + if (key) + *key = ((struct hashmap_entry*) *state)->key; + + return ((struct hashmap_entry*) *state)->value; +} diff --git a/src/polypcore/hashmap.h b/src/polypcore/hashmap.h new file mode 100644 index 00000000..14f82705 --- /dev/null +++ b/src/polypcore/hashmap.h @@ -0,0 +1,53 @@ +#ifndef foohashmaphfoo +#define foohashmaphfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* Simple Implementation of a hash table. Memory management is the + * user's job. It's a good idea to have the key pointer point to a + * string in the value data. */ + +typedef struct pa_hashmap pa_hashmap; + +/* Create a new hashmap. Use the specified functions for hashing and comparing objects in the map */ +pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)); + +/* Free the hash table. Calls the specified function for every value in the table. The function may be NULL */ +void pa_hashmap_free(pa_hashmap*, void (*free_func)(void *p, void *userdata), void *userdata); + +/* Returns non-zero when the entry already exists */ +int pa_hashmap_put(pa_hashmap *h, const void *key, void *value); +void* pa_hashmap_get(pa_hashmap *h, const void *key); + +/* Returns the data of the entry while removing */ +void* pa_hashmap_remove(pa_hashmap *h, const void *key); + +unsigned pa_hashmap_size(pa_hashmap *h); + +/* May be used to iterate through the hashmap. Initially the opaque + pointer *state has to be set to NULL. The hashmap may not be + modified during iteration. The key of the entry is returned in + *key, if key is non-NULL. After the last entry in the hashmap NULL + is returned. */ +void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key); + +#endif diff --git a/src/polypcore/howl-wrap.c b/src/polypcore/howl-wrap.c new file mode 100644 index 00000000..77d096ac --- /dev/null +++ b/src/polypcore/howl-wrap.c @@ -0,0 +1,116 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "howl-wrap.h" +#include "log.h" +#include "xmalloc.h" +#include "props.h" + +#define HOWL_PROPERTY "howl" + +pa_howl_wrapper { + pa_core *core; + int ref; + + pa_io_event *io_event; + sw_discovery discovery; + +}; + +static void howl_io_event(pa_mainloop_api*m, pa_io_event *e, int fd, pa_io_event_flags f, void *userdata) { + pa_howl_wrapper *w = userdata; + assert(m && e && fd >= 0 && w && w->ref >= 1); + + if (f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) + goto fail; + + if (sw_discovery_read_socket(w->discovery) != SW_OKAY) + goto fail; + + return; + +fail: + pa_log(__FILE__": howl connection died.\n"); + w->core->mainloop->io_free(w->io_event); + w->io_event = NULL; +} + +static pa_howl_wrapper* howl_wrapper_new(pa_core *c) { + pa_howl_wrapper *h; + sw_discovery session; + assert(c); + + if (sw_discovery_init(&session) != SW_OKAY) { + pa_log("sw_discovery_init() failed.\n"); + return NULL; + } + + h = pa_xmalloc(sizeof(pa_howl_wrapper)); + h->core = c; + h->ref = 1; + h->discovery = session; + + h->io_event = c->mainloop->io_new(c->mainloop, sw_discovery_socket(session), PA_IO_EVENT_INPUT, howl_io_event, h); + + return h; +} + +static void howl_wrapper_free(pa_howl_wrapper *h) { + assert(h); + + sw_discovery_fina(h->discovery); + + if (h->io_event) + h->core->mainloop->io_free(h->io_event); + + pa_xfree(h); +} + +pa_howl_wrapper* pa_howl_wrapper_get(pa_core *c) { + pa_howl_wrapper *h; + assert(c); + + if ((h = pa_property_get(c, HOWL_PROPERTY))) + return pa_howl_wrapper_ref(h); + + return howl_wrapper_new(c); +} + +pa_howl_wrapper* pa_howl_wrapper_ref(pa_howl_wrapper *h) { + assert(h && h->ref >= 1); + h->ref++; + return h; +} + +void pa_howl_wrapper_unref(pa_howl_wrapper *h) { + assert(h && h->ref >= 1); + if (!(--h->ref)) + howl_wrapper_free(h); +} + +sw_discovery pa_howl_wrapper_get_discovery(pa_howl_wrapper *h) { + assert(h && h->ref >= 1); + + return h->discovery; +} + diff --git a/src/polypcore/howl-wrap.h b/src/polypcore/howl-wrap.h new file mode 100644 index 00000000..a670b082 --- /dev/null +++ b/src/polypcore/howl-wrap.h @@ -0,0 +1,37 @@ +#ifndef foohowlwrapperhfoo +#define foohowlwrapperhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "core.h" + +pa_howl_wrapper; + +pa_howl_wrapper* pa_howl_wrapper_get(pa_core *c); +pa_howl_wrapper* pa_howl_wrapper_ref(pa_howl_wrapper *h); +void pa_howl_wrapper_unref(pa_howl_wrapper *h); + +sw_discovery pa_howl_wrapper_get_discovery(pa_howl_wrapper *h); + +#endif diff --git a/src/polypcore/idxset.c b/src/polypcore/idxset.c new file mode 100644 index 00000000..409d1fab --- /dev/null +++ b/src/polypcore/idxset.c @@ -0,0 +1,390 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "idxset.h" +#include "xmalloc.h" + +typedef struct idxset_entry { + void *data; + uint32_t index; + unsigned hash_value; + + struct idxset_entry *hash_prev, *hash_next; + struct idxset_entry* iterate_prev, *iterate_next; +} idxset_entry; + +struct pa_idxset { + unsigned (*hash_func) (const void *p); + int (*compare_func)(const void *a, const void *b); + + unsigned hash_table_size, n_entries; + idxset_entry **hash_table, **array, *iterate_list_head, *iterate_list_tail; + uint32_t index, start_index, array_size; +}; + +unsigned pa_idxset_string_hash_func(const void *p) { + unsigned hash = 0; + const char *c; + + for (c = p; *c; c++) + hash = 31 * hash + *c; + + return hash; +} + +int pa_idxset_string_compare_func(const void *a, const void *b) { + return strcmp(a, b); +} + +unsigned pa_idxset_trivial_hash_func(const void *p) { + return (unsigned) p; +} + +int pa_idxset_trivial_compare_func(const void *a, const void *b) { + return a != b; +} + +pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) { + pa_idxset *s; + + s = pa_xnew(pa_idxset, 1); + s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func; + s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func; + s->hash_table_size = 1023; + s->hash_table = pa_xmalloc0(sizeof(idxset_entry*)*s->hash_table_size); + s->array = NULL; + s->array_size = 0; + s->index = 0; + s->start_index = 0; + s->n_entries = 0; + + s->iterate_list_head = s->iterate_list_tail = NULL; + + return s; +} + +void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata) { + assert(s); + + while (s->iterate_list_head) { + idxset_entry *e = s->iterate_list_head; + s->iterate_list_head = s->iterate_list_head->iterate_next; + + if (free_func) + free_func(e->data, userdata); + pa_xfree(e); + } + + pa_xfree(s->hash_table); + pa_xfree(s->array); + pa_xfree(s); +} + +static idxset_entry* hash_scan(pa_idxset *s, idxset_entry* e, const void *p) { + assert(p); + + assert(s->compare_func); + for (; e; e = e->hash_next) + if (s->compare_func(e->data, p) == 0) + return e; + + return NULL; +} + +static void extend_array(pa_idxset *s, uint32_t idx) { + uint32_t i, j, l; + idxset_entry** n; + assert(idx >= s->start_index); + + if (idx < s->start_index + s->array_size) + return; + + for (i = 0; i < s->array_size; i++) + if (s->array[i]) + break; + + l = idx - s->start_index - i + 100; + n = pa_xnew0(idxset_entry*, l); + + for (j = 0; j < s->array_size-i; j++) + n[j] = s->array[i+j]; + + pa_xfree(s->array); + + s->array = n; + s->array_size = l; + s->start_index += i; +} + +static idxset_entry** array_index(pa_idxset*s, uint32_t idx) { + if (idx >= s->start_index + s->array_size) + return NULL; + + if (idx < s->start_index) + return NULL; + + return s->array + (idx - s->start_index); +} + +int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) { + unsigned h; + idxset_entry *e, **a; + assert(s && p); + + assert(s->hash_func); + h = s->hash_func(p) % s->hash_table_size; + + assert(s->hash_table); + if ((e = hash_scan(s, s->hash_table[h], p))) { + if (idx) + *idx = e->index; + + return -1; + } + + e = pa_xmalloc(sizeof(idxset_entry)); + e->data = p; + e->index = s->index++; + e->hash_value = h; + + /* Insert into hash table */ + e->hash_next = s->hash_table[h]; + e->hash_prev = NULL; + if (s->hash_table[h]) + s->hash_table[h]->hash_prev = e; + s->hash_table[h] = e; + + /* Insert into array */ + extend_array(s, e->index); + a = array_index(s, e->index); + assert(a && !*a); + *a = e; + + /* Insert into linked list */ + e->iterate_next = NULL; + e->iterate_prev = s->iterate_list_tail; + if (s->iterate_list_tail) { + assert(s->iterate_list_head); + s->iterate_list_tail->iterate_next = e; + } else { + assert(!s->iterate_list_head); + s->iterate_list_head = e; + } + s->iterate_list_tail = e; + + s->n_entries++; + assert(s->n_entries >= 1); + + if (idx) + *idx = e->index; + + return 0; +} + +void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) { + idxset_entry **a; + assert(s); + + if (!(a = array_index(s, idx))) + return NULL; + + if (!*a) + return NULL; + + return (*a)->data; +} + +void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) { + unsigned h; + idxset_entry *e; + assert(s && p); + + assert(s->hash_func); + h = s->hash_func(p) % s->hash_table_size; + + assert(s->hash_table); + if (!(e = hash_scan(s, s->hash_table[h], p))) + return NULL; + + if (idx) + *idx = e->index; + + return e->data; +} + +static void remove_entry(pa_idxset *s, idxset_entry *e) { + idxset_entry **a; + assert(s && e); + + /* Remove from array */ + a = array_index(s, e->index); + assert(a && *a && *a == e); + *a = NULL; + + /* Remove from linked list */ + if (e->iterate_next) + e->iterate_next->iterate_prev = e->iterate_prev; + else + s->iterate_list_tail = e->iterate_prev; + + if (e->iterate_prev) + e->iterate_prev->iterate_next = e->iterate_next; + else + s->iterate_list_head = e->iterate_next; + + /* Remove from hash table */ + if (e->hash_next) + e->hash_next->hash_prev = e->hash_prev; + + if (e->hash_prev) + e->hash_prev->hash_next = e->hash_next; + else + s->hash_table[e->hash_value] = e->hash_next; + + pa_xfree(e); + + assert(s->n_entries >= 1); + s->n_entries--; +} + +void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) { + idxset_entry **a; + void *data; + + assert(s); + + if (!(a = array_index(s, idx))) + return NULL; + + data = (*a)->data; + remove_entry(s, *a); + + return data; +} + +void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) { + idxset_entry *e; + unsigned h; + void *r; + + assert(s->hash_func); + h = s->hash_func(data) % s->hash_table_size; + + assert(s->hash_table); + if (!(e = hash_scan(s, s->hash_table[h], data))) + return NULL; + + r = e->data; + if (idx) + *idx = e->index; + + remove_entry(s, e); + + return r; +} + +void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) { + idxset_entry **a, *e = NULL; + assert(s && idx); + + if ((a = array_index(s, *idx)) && *a) + e = (*a)->iterate_next; + + if (!e) + e = s->iterate_list_head; + + if (!e) + return NULL; + + *idx = e->index; + return e->data; +} + +void* pa_idxset_first(pa_idxset *s, uint32_t *idx) { + assert(s); + + if (!s->iterate_list_head) + return NULL; + + if (idx) + *idx = s->iterate_list_head->index; + return s->iterate_list_head->data; +} + +void *pa_idxset_next(pa_idxset *s, uint32_t *idx) { + idxset_entry **a, *e = NULL; + assert(s && idx); + + if ((a = array_index(s, *idx)) && *a) + e = (*a)->iterate_next; + + if (e) { + *idx = e->index; + return e->data; + } else { + *idx = PA_IDXSET_INVALID; + return NULL; + } +} + + +int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata) { + idxset_entry *e; + assert(s && func); + + e = s->iterate_list_head; + while (e) { + int del = 0, r; + idxset_entry *n = e->iterate_next; + + r = func(e->data, e->index, &del, userdata); + + if (del) + remove_entry(s, e); + + if (r < 0) + return r; + + e = n; + } + + return 0; +} + +unsigned pa_idxset_size(pa_idxset*s) { + assert(s); + return s->n_entries; +} + +int pa_idxset_isempty(pa_idxset *s) { + assert(s); + return s->n_entries == 0; +} + diff --git a/src/polypcore/idxset.h b/src/polypcore/idxset.h new file mode 100644 index 00000000..17ae16cb --- /dev/null +++ b/src/polypcore/idxset.h @@ -0,0 +1,94 @@ +#ifndef fooidxsethfoo +#define fooidxsethfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +/* A combination of a set and a dynamic array. Entries are indexable + * both through a numeric automatically generated index and the entry's + * data pointer. As usual, memory management is the user's job. */ + +/* A special index value denoting the invalid index. */ +#define PA_IDXSET_INVALID ((uint32_t) -1) + +/* Generic implementations for hash and comparison functions. Just + * compares the pointer or calculates the hash value directly from the + * pointer value. */ +unsigned pa_idxset_trivial_hash_func(const void *p); +int pa_idxset_trivial_compare_func(const void *a, const void *b); + +/* Generic implementations for hash and comparison functions for strings. */ +unsigned pa_idxset_string_hash_func(const void *p); +int pa_idxset_string_compare_func(const void *a, const void *b); + +typedef struct pa_idxset pa_idxset; + +/* Instantiate a new idxset with the specified hash and comparison functions */ +pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)); + +/* Free the idxset. When the idxset is not empty the specified function is called for every entry contained */ +void pa_idxset_free(pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata); + +/* Store a new item in the idxset. The index of the item is returned in *idx */ +int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx); + +/* Get the entry by its idx */ +void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx); + +/* Get the entry by its data. The idx is returned in *index */ +void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx); + +/* Similar to pa_idxset_get_by_index(), but removes the entry from the idxset. */ +void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx); + +/* Similar to pa_idxset_get_by_data(), but removes the entry from the idxset */ +void* pa_idxset_remove_by_data(pa_idxset*s, const void *p, uint32_t *idx); + +/* This may be used to iterate through all entries. When called with + an invalid index value it returns the first entry, otherwise the + next following. The function is best called with *idx = + PA_IDXSET_VALID first. It is safe to manipulate the idxset between + the calls. It is not guaranteed that all entries have already been + returned before the an entry is returned the second time.*/ +void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx); + +/* Return the oldest entry in the idxset. Fill in its index in *idx. */ +void* pa_idxset_first(pa_idxset *s, uint32_t *idx); + +/* Return the entry following the entry indexed by *idx. After the + * call *index contains the index of the returned + * object. pa_idxset_first() and pa_idxset_next() may be used to + * iterate through the set.*/ +void *pa_idxset_next(pa_idxset *s, uint32_t *idx); + +/* Call a function for every item in the set. If the callback function + returns -1, the loop is terminated. If *del is set to non-zero that + specific item is removed. It is not safe to call any other + functions on the idxset while pa_idxset_foreach is executed. */ +int pa_idxset_foreach(pa_idxset*s, int (*func)(void *p, uint32_t idx, int *del, void*userdata), void *userdata); + +unsigned pa_idxset_size(pa_idxset*s); + +int pa_idxset_isempty(pa_idxset *s); + +#endif diff --git a/src/polypcore/inet_ntop.c b/src/polypcore/inet_ntop.c new file mode 100644 index 00000000..a25c3c95 --- /dev/null +++ b/src/polypcore/inet_ntop.c @@ -0,0 +1,80 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#ifndef HAVE_INET_NTOP + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#include "winsock.h" + +#include "inet_ntop.h" + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) { + struct in_addr *in = (struct in_addr*)src; + struct in6_addr *in6 = (struct in6_addr*)src; + + assert(src && dst); + + switch (af) { + case AF_INET: + snprintf(dst, cnt, "%d.%d.%d.%d", +#ifdef WORDS_BIGENDIAN + (int)(in->s_addr >> 24) & 0xff, + (int)(in->s_addr >> 16) & 0xff, + (int)(in->s_addr >> 8) & 0xff, + (int)(in->s_addr >> 0) & 0xff); +#else + (int)(in->s_addr >> 0) & 0xff, + (int)(in->s_addr >> 8) & 0xff, + (int)(in->s_addr >> 16) & 0xff, + (int)(in->s_addr >> 24) & 0xff); +#endif + break; + case AF_INET6: + snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x", + in6->s6_addr[ 0] << 8 | in6->s6_addr[ 1], + in6->s6_addr[ 2] << 8 | in6->s6_addr[ 3], + in6->s6_addr[ 4] << 8 | in6->s6_addr[ 5], + in6->s6_addr[ 6] << 8 | in6->s6_addr[ 7], + in6->s6_addr[ 8] << 8 | in6->s6_addr[ 9], + in6->s6_addr[10] << 8 | in6->s6_addr[11], + in6->s6_addr[12] << 8 | in6->s6_addr[13], + in6->s6_addr[14] << 8 | in6->s6_addr[15]); + break; + default: + errno = EAFNOSUPPORT; + return NULL; + } + + return dst; +} + +#endif /* INET_NTOP */ diff --git a/src/polypcore/inet_ntop.h b/src/polypcore/inet_ntop.h new file mode 100644 index 00000000..7fb67b44 --- /dev/null +++ b/src/polypcore/inet_ntop.h @@ -0,0 +1,12 @@ +#ifndef fooinet_ntophfoo +#define fooinet_ntophfoo + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#include "winsock.h" + +const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt); + +#endif diff --git a/src/polypcore/iochannel.c b/src/polypcore/iochannel.c new file mode 100644 index 00000000..273d47e0 --- /dev/null +++ b/src/polypcore/iochannel.c @@ -0,0 +1,292 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "winsock.h" + +#include "iochannel.h" +#include "util.h" +#include "socket-util.h" +#include "xmalloc.h" + +struct pa_iochannel { + int ifd, ofd; + pa_mainloop_api* mainloop; + + pa_iochannel_callback_t callback; + void*userdata; + + int readable; + int writable; + int hungup; + + int no_close; + + pa_io_event* input_event, *output_event; +}; + +static void enable_mainloop_sources(pa_iochannel *io) { + assert(io); + + if (io->input_event == io->output_event && io->input_event) { + pa_io_event_flags_t f = PA_IO_EVENT_NULL; + assert(io->input_event); + + if (!io->readable) + f |= PA_IO_EVENT_INPUT; + if (!io->writable) + f |= PA_IO_EVENT_OUTPUT; + + io->mainloop->io_enable(io->input_event, f); + } else { + if (io->input_event) + io->mainloop->io_enable(io->input_event, io->readable ? PA_IO_EVENT_NULL : PA_IO_EVENT_INPUT); + if (io->output_event) + io->mainloop->io_enable(io->output_event, io->writable ? PA_IO_EVENT_NULL : PA_IO_EVENT_OUTPUT); + } +} + +static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) { + pa_iochannel *io = userdata; + int changed = 0; + + assert(m); + assert(e); + assert(fd >= 0); + assert(userdata); + + if ((f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) && !io->hungup) { + io->hungup = 1; + changed = 1; + + if (e == io->input_event) { + io->mainloop->io_free(io->input_event); + io->input_event = NULL; + + if (io->output_event == e) + io->output_event = NULL; + } else if (e == io->output_event) { + io->mainloop->io_free(io->output_event); + io->output_event = NULL; + } + } else { + + if ((f & PA_IO_EVENT_INPUT) && !io->readable) { + io->readable = 1; + changed = 1; + assert(e == io->input_event); + } + + if ((f & PA_IO_EVENT_OUTPUT) && !io->writable) { + io->writable = 1; + changed = 1; + assert(e == io->output_event); + } + } + + if (changed) { + enable_mainloop_sources(io); + + if (io->callback) + io->callback(io, io->userdata); + } +} + +pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) { + pa_iochannel *io; + + assert(m); + assert(ifd >= 0 || ofd >= 0); + + io = pa_xnew(pa_iochannel, 1); + io->ifd = ifd; + io->ofd = ofd; + io->mainloop = m; + + io->userdata = NULL; + io->callback = NULL; + io->readable = 0; + io->writable = 0; + io->hungup = 0; + io->no_close = 0; + + io->input_event = io->output_event = NULL; + + if (ifd == ofd) { + assert(ifd >= 0); + pa_make_nonblock_fd(io->ifd); + io->input_event = io->output_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT|PA_IO_EVENT_OUTPUT, callback, io); + } else { + + if (ifd >= 0) { + pa_make_nonblock_fd(io->ifd); + io->input_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT, callback, io); + } + + if (ofd >= 0) { + pa_make_nonblock_fd(io->ofd); + io->output_event = m->io_new(m, ofd, PA_IO_EVENT_OUTPUT, callback, io); + } + } + + return io; +} + +void pa_iochannel_free(pa_iochannel*io) { + assert(io); + + if (io->input_event) + io->mainloop->io_free(io->input_event); + + if (io->output_event && (io->output_event != io->input_event)) + io->mainloop->io_free(io->output_event); + + if (!io->no_close) { + if (io->ifd >= 0) + + close(io->ifd); + if (io->ofd >= 0 && io->ofd != io->ifd) + close(io->ofd); + } + + pa_xfree(io); +} + +int pa_iochannel_is_readable(pa_iochannel*io) { + assert(io); + + return io->readable || io->hungup; +} + +int pa_iochannel_is_writable(pa_iochannel*io) { + assert(io); + + return io->writable && !io->hungup; +} + +int pa_iochannel_is_hungup(pa_iochannel*io) { + assert(io); + + return io->hungup; +} + +ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) { + ssize_t r; + + assert(io); + assert(data); + assert(l); + assert(io->ofd >= 0); + +#ifdef OS_IS_WIN32 + r = send(io->ofd, data, l, 0); + if (r < 0) { + if (WSAGetLastError() != WSAENOTSOCK) { + errno = WSAGetLastError(); + return r; + } + } + + if (r < 0) +#endif + r = write(io->ofd, data, l); + if (r >= 0) { + io->writable = 0; + enable_mainloop_sources(io); + } + + return r; +} + +ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) { + ssize_t r; + + assert(io); + assert(data); + assert(io->ifd >= 0); + +#ifdef OS_IS_WIN32 + r = recv(io->ifd, data, l, 0); + if (r < 0) { + if (WSAGetLastError() != WSAENOTSOCK) { + errno = WSAGetLastError(); + return r; + } + } + + if (r < 0) +#endif + r = read(io->ifd, data, l); + + if (r >= 0) { + io->readable = 0; + enable_mainloop_sources(io); + } + + return r; +} + +void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_callback_t _callback, void *userdata) { + assert(io); + + io->callback = _callback; + io->userdata = userdata; +} + +void pa_iochannel_set_noclose(pa_iochannel*io, int b) { + assert(io); + + io->no_close = b; +} + +void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l) { + assert(io); + assert(s); + assert(l); + + pa_socket_peer_to_string(io->ifd, s, l); +} + +int pa_iochannel_socket_set_rcvbuf(pa_iochannel *io, size_t l) { + assert(io); + + return pa_socket_set_rcvbuf(io->ifd, l); +} + +int pa_iochannel_socket_set_sndbuf(pa_iochannel *io, size_t l) { + assert(io); + + return pa_socket_set_sndbuf(io->ofd, l); +} + +pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io) { + assert(io); + + return io->mainloop; +} diff --git a/src/polypcore/iochannel.h b/src/polypcore/iochannel.h new file mode 100644 index 00000000..7d355d8f --- /dev/null +++ b/src/polypcore/iochannel.h @@ -0,0 +1,72 @@ +#ifndef fooiochannelhfoo +#define fooiochannelhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +/* A wrapper around UNIX file descriptors for attaching them to the a + main event loop. Everytime new data may be read or be written to + the channel a callback function is called. It is safe to destroy + the calling iochannel object from the callback */ + +/* When pa_iochannel_is_readable() returns non-zero, the user has to + * call this function in a loop until it is no longer set or EOF + * reached. Otherwise strange things may happen when an EOF is + * reached. */ + +typedef struct pa_iochannel pa_iochannel; + +/* Create a new IO channel for the specified file descriptors for +input resp. output. It is safe to pass the same file descriptor for +both parameters (in case of full-duplex channels). For a simplex +channel specify -1 for the other direction. */ + +pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd); +void pa_iochannel_free(pa_iochannel*io); + +ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l); +ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l); + +int pa_iochannel_is_readable(pa_iochannel*io); +int pa_iochannel_is_writable(pa_iochannel*io); +int pa_iochannel_is_hungup(pa_iochannel*io); + +/* Don't close the file descirptors when the io channel is freed. By + * default the file descriptors are closed. */ +void pa_iochannel_set_noclose(pa_iochannel*io, int b); + +/* Set the callback function that is called whenever data becomes available for read or write */ +typedef void (*pa_iochannel_callback_t)(pa_iochannel*io, void *userdata); +void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_callback_t callback, void *userdata); + +/* In case the file descriptor is a socket, return a pretty-printed string in *s which describes the peer connected */ +void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l); + +/* Use setsockopt() to tune the recieve and send buffers of TCP sockets */ +int pa_iochannel_socket_set_rcvbuf(pa_iochannel*io, size_t l); +int pa_iochannel_socket_set_sndbuf(pa_iochannel*io, size_t l); + +pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io); + +#endif diff --git a/src/polypcore/ioline.c b/src/polypcore/ioline.c new file mode 100644 index 00000000..5b669f5c --- /dev/null +++ b/src/polypcore/ioline.c @@ -0,0 +1,368 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "ioline.h" +#include "xmalloc.h" +#include "log.h" + +#define BUFFER_LIMIT (64*1024) +#define READ_SIZE (1024) + +struct pa_ioline { + pa_iochannel *io; + pa_defer_event *defer_event; + pa_mainloop_api *mainloop; + int ref; + int dead; + + char *wbuf; + size_t wbuf_length, wbuf_index, wbuf_valid_length; + + char *rbuf; + size_t rbuf_length, rbuf_index, rbuf_valid_length; + + void (*callback)(pa_ioline*io, const char *s, void *userdata); + void *userdata; + + int defer_close; +}; + +static void io_callback(pa_iochannel*io, void *userdata); +static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata); + +pa_ioline* pa_ioline_new(pa_iochannel *io) { + pa_ioline *l; + assert(io); + + l = pa_xmalloc(sizeof(pa_ioline)); + l->io = io; + l->dead = 0; + + l->wbuf = NULL; + l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0; + + l->rbuf = NULL; + l->rbuf_length = l->rbuf_index = l->rbuf_valid_length = 0; + + l->callback = NULL; + l->userdata = NULL; + l->ref = 1; + + l->mainloop = pa_iochannel_get_mainloop_api(io); + + l->defer_event = l->mainloop->defer_new(l->mainloop, defer_callback, l); + l->mainloop->defer_enable(l->defer_event, 0); + + l->defer_close = 0; + + pa_iochannel_set_callback(io, io_callback, l); + + return l; +} + +static void ioline_free(pa_ioline *l) { + assert(l); + + if (l->io) + pa_iochannel_free(l->io); + + if (l->defer_event) + l->mainloop->defer_free(l->defer_event); + + pa_xfree(l->wbuf); + pa_xfree(l->rbuf); + pa_xfree(l); +} + +void pa_ioline_unref(pa_ioline *l) { + assert(l && l->ref >= 1); + + if ((--l->ref) <= 0) + ioline_free(l); +} + +pa_ioline* pa_ioline_ref(pa_ioline *l) { + assert(l && l->ref >= 1); + + l->ref++; + return l; +} + +void pa_ioline_close(pa_ioline *l) { + assert(l && l->ref >= 1); + + l->dead = 1; + if (l->io) { + pa_iochannel_free(l->io); + l->io = NULL; + } + + if (l->defer_event) { + l->mainloop->defer_free(l->defer_event); + l->defer_event = NULL; + } +} + +void pa_ioline_puts(pa_ioline *l, const char *c) { + size_t len; + assert(l && c && l->ref >= 1 && !l->dead); + + pa_ioline_ref(l); + + len = strlen(c); + if (len > BUFFER_LIMIT - l->wbuf_valid_length) + len = BUFFER_LIMIT - l->wbuf_valid_length; + + if (len) { + assert(l->wbuf_length >= l->wbuf_valid_length); + + /* In case the allocated buffer is too small, enlarge it. */ + if (l->wbuf_valid_length + len > l->wbuf_length) { + size_t n = l->wbuf_valid_length+len; + char *new = pa_xmalloc(n); + if (l->wbuf) { + memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length); + pa_xfree(l->wbuf); + } + l->wbuf = new; + l->wbuf_length = n; + l->wbuf_index = 0; + } else if (l->wbuf_index + l->wbuf_valid_length + len > l->wbuf_length) { + + /* In case the allocated buffer fits, but the current index is too far from the start, move it to the front. */ + memmove(l->wbuf, l->wbuf+l->wbuf_index, l->wbuf_valid_length); + l->wbuf_index = 0; + } + + assert(l->wbuf_index + l->wbuf_valid_length + len <= l->wbuf_length); + + /* Append the new string */ + memcpy(l->wbuf + l->wbuf_index + l->wbuf_valid_length, c, len); + l->wbuf_valid_length += len; + + l->mainloop->defer_enable(l->defer_event, 1); + } + + pa_ioline_unref(l); +} + +void pa_ioline_set_callback(pa_ioline*l, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata) { + assert(l && l->ref >= 1); + l->callback = callback; + l->userdata = userdata; +} + +static void failure(pa_ioline *l) { + assert(l && l->ref >= 1 && !l->dead); + + pa_ioline_close(l); + + if (l->callback) { + l->callback(l, NULL, l->userdata); + l->callback = NULL; + } +} + +static void scan_for_lines(pa_ioline *l, size_t skip) { + assert(l && l->ref >= 1 && skip < l->rbuf_valid_length); + + while (!l->dead && l->rbuf_valid_length > skip) { + char *e, *p; + size_t m; + + if (!(e = memchr(l->rbuf + l->rbuf_index + skip, '\n', l->rbuf_valid_length - skip))) + break; + + *e = 0; + + p = l->rbuf + l->rbuf_index; + m = strlen(p); + + l->rbuf_index += m+1; + l->rbuf_valid_length -= m+1; + + /* A shortcut for the next time */ + if (l->rbuf_valid_length == 0) + l->rbuf_index = 0; + + if (l->callback) + l->callback(l, p, l->userdata); + + skip = 0; + } + + /* If the buffer became too large and still no newline was found, drop it. */ + if (l->rbuf_valid_length >= BUFFER_LIMIT) + l->rbuf_index = l->rbuf_valid_length = 0; +} + +static int do_write(pa_ioline *l); + +static int do_read(pa_ioline *l) { + assert(l && l->ref >= 1); + + while (!l->dead && pa_iochannel_is_readable(l->io)) { + ssize_t r; + size_t len; + + len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length; + + /* Check if we have to enlarge the read buffer */ + if (len < READ_SIZE) { + size_t n = l->rbuf_valid_length+READ_SIZE; + + if (n >= BUFFER_LIMIT) + n = BUFFER_LIMIT; + + if (l->rbuf_length >= n) { + /* The current buffer is large enough, let's just move the data to the front */ + if (l->rbuf_valid_length) + memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length); + } else { + /* Enlarge the buffer */ + char *new = pa_xmalloc(n); + if (l->rbuf_valid_length) + memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length); + pa_xfree(l->rbuf); + l->rbuf = new; + l->rbuf_length = n; + } + + l->rbuf_index = 0; + } + + len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length; + + assert(len >= READ_SIZE); + + /* Read some data */ + r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len); + if (r == 0) { + /* Got an EOF, so fake an exit command. */ + l->rbuf_index = 0; + snprintf (l->rbuf, l->rbuf_length, "exit\n"); + r = 5; + pa_ioline_puts(l, "\nExiting.\n"); + do_write(l); + } else if (r < 0) { + pa_log(__FILE__": read() failed: %s\n", strerror(errno)); + failure(l); + return -1; + } + + l->rbuf_valid_length += r; + + /* Look if a line has been terminated in the newly read data */ + scan_for_lines(l, l->rbuf_valid_length - r); + } + + return 0; +} + +/* Try to flush the buffer */ +static int do_write(pa_ioline *l) { + ssize_t r; + assert(l && l->ref >= 1); + + while (!l->dead && pa_iochannel_is_writable(l->io) && l->wbuf_valid_length) { + + if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0) { + pa_log(__FILE__": write() failed: %s\n", r < 0 ? strerror(errno) : "EOF"); + failure(l); + return -1; + } + + l->wbuf_index += r; + l->wbuf_valid_length -= r; + + /* A shortcut for the next time */ + if (l->wbuf_valid_length == 0) + l->wbuf_index = 0; + } + + return 0; +} + +/* Try to flush read/write data */ +static void do_work(pa_ioline *l) { + assert(l && l->ref >= 1); + + pa_ioline_ref(l); + + l->mainloop->defer_enable(l->defer_event, 0); + + if (!l->dead) + do_write(l); + + if (!l->dead) + do_read(l); + + if (l->defer_close && !l->wbuf_valid_length) + failure(l); + + pa_ioline_unref(l); +} + +static void io_callback(pa_iochannel*io, void *userdata) { + pa_ioline *l = userdata; + assert(io && l && l->ref >= 1); + + do_work(l); +} + +static void defer_callback(pa_mainloop_api*m, pa_defer_event*e, void *userdata) { + pa_ioline *l = userdata; + assert(l && l->ref >= 1 && l->mainloop == m && l->defer_event == e); + + do_work(l); +} + +void pa_ioline_defer_close(pa_ioline *l) { + assert(l); + + l->defer_close = 1; + + if (!l->wbuf_valid_length) + l->mainloop->defer_enable(l->defer_event, 1); +} + +void pa_ioline_printf(pa_ioline *s, const char *format, ...) { + char *t; + va_list ap; + + + va_start(ap, format); + t = pa_vsprintf_malloc(format, ap); + va_end(ap); + + pa_ioline_puts(s, t); + pa_xfree(t); +} diff --git a/src/polypcore/ioline.h b/src/polypcore/ioline.h new file mode 100644 index 00000000..84ccb47a --- /dev/null +++ b/src/polypcore/ioline.h @@ -0,0 +1,51 @@ +#ifndef fooiolinehfoo +#define fooiolinehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "iochannel.h" +#include "util.h" + +/* An ioline wraps an iochannel for line based communication. A + * callback function is called whenever a new line has been recieved + * from the client */ + +typedef struct pa_ioline pa_ioline; + +pa_ioline* pa_ioline_new(pa_iochannel *io); +void pa_ioline_unref(pa_ioline *l); +pa_ioline* pa_ioline_ref(pa_ioline *l); +void pa_ioline_close(pa_ioline *l); + +/* Write a string to the channel */ +void pa_ioline_puts(pa_ioline *s, const char *c); + +/* Write a string to the channel */ +void pa_ioline_printf(pa_ioline *s, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3); + +/* Set the callback function that is called for every recieved line */ +void pa_ioline_set_callback(pa_ioline*io, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata); + +/* Make sure to close the ioline object as soon as the send buffer is emptied */ +void pa_ioline_defer_close(pa_ioline *io); + +#endif diff --git a/src/polypcore/llist.h b/src/polypcore/llist.h new file mode 100644 index 00000000..eb8cd017 --- /dev/null +++ b/src/polypcore/llist.h @@ -0,0 +1,69 @@ +#ifndef foollistfoo +#define foollistfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* Some macros for maintaining doubly linked lists */ + +/* The head of the linked list. Use this in the structure that shall + * contain the head of the linked list */ +#define PA_LLIST_HEAD(t,name) t *name + +/* The pointers in the linked list's items. Use this in the item structure */ +#define PA_LLIST_FIELDS(t) t *next, *prev + +/* Initialize the list's head */ +#define PA_LLIST_HEAD_INIT(t,item) do { (item) = NULL; } while(0) + +/* Initialize a list item */ +#define PA_LLIST_INIT(t,item) do { \ + t *_item = (item); \ + assert(_item); \ + _item->prev = _item->next = NULL; \ + } while(0) + +/* Prepend an item to the list */ +#define PA_LLIST_PREPEND(t,head,item) do { \ + t **_head = &(head), *_item = (item); \ + assert(_item); \ + if ((_item->next = *_head)) \ + _item->next->prev = _item; \ + _item->prev = NULL; \ + *_head = _item; \ + } while (0) + +/* Remove an item from the list */ +#define PA_LLIST_REMOVE(t,head,item) do { \ + t **_head = &(head), *_item = (item); \ + assert(_item); \ + if (_item->next) \ + _item->next->prev = _item->prev; \ + if (_item->prev) \ + _item->prev->next = _item->next; \ + else {\ + assert(*_head == _item); \ + *_head = _item->next; \ + } \ + _item->next = _item->prev = NULL; \ + } while(0) + +#endif diff --git a/src/polypcore/log.c b/src/polypcore/log.c new file mode 100644 index 00000000..97406f79 --- /dev/null +++ b/src/polypcore/log.c @@ -0,0 +1,150 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#ifdef HAVE_SYSLOG_H +#include +#endif + +#include "log.h" +#include "xmalloc.h" +#include "util.h" + +#define ENV_LOGLEVEL "POLYP_LOG" + +static char *log_ident = NULL; +static pa_log_target_t log_target = PA_LOG_STDERR; +static void (*user_log_func)(pa_log_level_t l, const char *s) = NULL; +static pa_log_level_t maximal_level = PA_LOG_NOTICE; + +#ifdef HAVE_SYSLOG_H +static const int level_to_syslog[] = { + [PA_LOG_ERROR] = LOG_ERR, + [PA_LOG_WARN] = LOG_WARNING, + [PA_LOG_NOTICE] = LOG_NOTICE, + [PA_LOG_INFO] = LOG_INFO, + [PA_LOG_DEBUG] = LOG_DEBUG +}; +#endif + +void pa_log_set_ident(const char *p) { + if (log_ident) + pa_xfree(log_ident); + + log_ident = pa_xstrdup(p); +} + +void pa_log_set_maximal_level(pa_log_level_t l) { + assert(l < PA_LOG_LEVEL_MAX); + maximal_level = l; +} + +void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t l, const char*s)) { + assert(t == PA_LOG_USER || !func); + log_target = t; + user_log_func = func; +} + +void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) { + const char *e; + assert(level < PA_LOG_LEVEL_MAX); + + if ((e = getenv(ENV_LOGLEVEL))) + maximal_level = atoi(e); + + if (level > maximal_level) + return; + + switch (log_target) { + case PA_LOG_STDERR: + vfprintf(stderr, format, ap); + break; + +#ifdef HAVE_SYSLOG_H + case PA_LOG_SYSLOG: + openlog(log_ident ? log_ident : "???", LOG_PID, LOG_USER); + vsyslog(level_to_syslog[level], format, ap); + closelog(); + break; +#endif + + case PA_LOG_USER: { + char *t = pa_vsprintf_malloc(format, ap); + assert(user_log_func); + user_log_func(level, t); + pa_xfree(t); + } + + case PA_LOG_NULL: + default: + break; + } + +} + +void pa_log_level(pa_log_level_t level, const char *format, ...) { + va_list ap; + va_start(ap, format); + pa_log_levelv(level, format, ap); + va_end(ap); +} + +void pa_log_debug(const char *format, ...) { + va_list ap; + va_start(ap, format); + pa_log_levelv(PA_LOG_DEBUG, format, ap); + va_end(ap); +} + +void pa_log_info(const char *format, ...) { + va_list ap; + va_start(ap, format); + pa_log_levelv(PA_LOG_INFO, format, ap); + va_end(ap); +} + +void pa_log_notice(const char *format, ...) { + va_list ap; + va_start(ap, format); + pa_log_levelv(PA_LOG_INFO, format, ap); + va_end(ap); +} + +void pa_log_warn(const char *format, ...) { + va_list ap; + va_start(ap, format); + pa_log_levelv(PA_LOG_WARN, format, ap); + va_end(ap); +} + +void pa_log_error(const char *format, ...) { + va_list ap; + va_start(ap, format); + pa_log_levelv(PA_LOG_ERROR, format, ap); + va_end(ap); +} diff --git a/src/polypcore/log.h b/src/polypcore/log.h new file mode 100644 index 00000000..ce8aea98 --- /dev/null +++ b/src/polypcore/log.h @@ -0,0 +1,69 @@ +#ifndef foologhfoo +#define foologhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include "gccmacro.h" + +/* A simple logging subsystem */ + +/* Where to log to */ +typedef enum pa_log_target { + PA_LOG_STDERR, /* default */ + PA_LOG_SYSLOG, + PA_LOG_USER, /* to user specified function */ + PA_LOG_NULL /* to /dev/null */ +} pa_log_target_t; + +typedef enum pa_log_level { + PA_LOG_ERROR = 0, /* Error messages */ + PA_LOG_WARN = 1, /* Warning messages */ + PA_LOG_NOTICE = 2, /* Notice messages */ + PA_LOG_INFO = 3, /* Info messages */ + PA_LOG_DEBUG = 4, /* debug message */ + PA_LOG_LEVEL_MAX +} pa_log_level_t; + +/* Set an identification for the current daemon. Used when logging to syslog. */ +void pa_log_set_ident(const char *p); + +/* Set another log target. If t is PA_LOG_USER you may specify a function that is called every log string */ +void pa_log_set_target(pa_log_target_t t, void (*func)(pa_log_level_t t, const char*s)); + +/* Minimal log level */ +void pa_log_set_maximal_level(pa_log_level_t l); + +/* Do a log line */ +void pa_log_debug(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); +void pa_log_info(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); +void pa_log_notice(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); +void pa_log_warn(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); +void pa_log_error(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); + +void pa_log_level(pa_log_level_t level, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3); + +void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap); + +#define pa_log pa_log_error + +#endif diff --git a/src/polypcore/mcalign.c b/src/polypcore/mcalign.c new file mode 100644 index 00000000..4d765625 --- /dev/null +++ b/src/polypcore/mcalign.c @@ -0,0 +1,188 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "mcalign.h" +#include "xmalloc.h" + +struct pa_mcalign { + size_t base; + pa_memchunk leftover, current; + pa_memblock_stat *memblock_stat; +}; + +pa_mcalign *pa_mcalign_new(size_t base, pa_memblock_stat *s) { + pa_mcalign *m; + assert(base); + + m = pa_xnew(pa_mcalign, 1); + m->base = base; + pa_memchunk_reset(&m->leftover); + pa_memchunk_reset(&m->current); + m->memblock_stat = s; + + return m; +} + +void pa_mcalign_free(pa_mcalign *m) { + assert(m); + + if (m->leftover.memblock) + pa_memblock_unref(m->leftover.memblock); + + if (m->current.memblock) + pa_memblock_unref(m->current.memblock); + + pa_xfree(m); +} + +void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c) { + assert(m && c && c->memblock && c->length); + + /* Append to the leftover memory block */ + if (m->leftover.memblock) { + assert(!m->current.memblock); + + /* Try to merge */ + if (m->leftover.memblock == c->memblock && + m->leftover.index + m->leftover.length == c->index) { + + /* Merge */ + m->leftover.length += c->length; + + /* If the new chunk is larger than m->base, move it to current */ + if (m->leftover.length >= m->base) { + m->current = m->leftover; + pa_memchunk_reset(&m->leftover); + } + + } else { + size_t l; + + /* We have to copy */ + assert(m->leftover.length < m->base); + l = m->base - m->leftover.length; + + if (l > c->length) + l = c->length; + + /* Can we use the current block? */ + pa_memchunk_make_writable(&m->leftover, m->memblock_stat, m->base); + + memcpy((uint8_t*) m->leftover.memblock->data + m->leftover.index + m->leftover.length, (uint8_t*) c->memblock->data + c->index, l); + m->leftover.length += l; + + assert(m->leftover.length <= m->base && m->leftover.length <= m->leftover.memblock->length); + + if (c->length > l) { + /* Save the remainder of the memory block */ + m->current = *c; + m->current.index += l; + m->current.length -= l; + pa_memblock_ref(m->current.memblock); + } + } + } else { + assert(!m->leftover.memblock && !m->current.memblock); + + /* Nothing to merge or copy, just store it */ + + if (c->length >= m->base) + m->current = *c; + else + m->leftover = *c; + + pa_memblock_ref(c->memblock); + } +} + +int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) { + assert(m && c); + + /* First test if there's a leftover memory block available */ + if (m->leftover.memblock) { + assert(m->leftover.length > 0 && m->leftover.length <= m->base); + + /* The leftover memory block is not yet complete */ + if (m->leftover.length < m->base) + return -1; + + /* Return the leftover memory block */ + *c = m->leftover; + pa_memchunk_reset(&m->leftover); + + /* If the current memblock is too small move it the leftover */ + if (m->current.memblock && m->current.length < m->base) { + m->leftover = m->current; + pa_memchunk_reset(&m->current); + } + + return 0; + } + + /* Now let's see if there is other data available */ + if (m->current.memblock) { + size_t l; + assert(m->current.length >= m->base); + + /* The length of the returned memory block */ + l = m->current.length; + l /= m->base; + l *= m->base; + assert(l > 0); + + /* Prepare the returned block */ + *c = m->current; + pa_memblock_ref(c->memblock); + c->length = l; + + /* Drop that from the current memory block */ + assert(l <= m->current.length); + m->current.index += l; + m->current.length -= l; + + /* In case the whole block was dropped ... */ + if (m->current.length == 0) + pa_memblock_unref(m->current.memblock); + else { + /* Move the raimainder to leftover */ + assert(m->current.length < m->base && !m->leftover.memblock); + + m->leftover = m->current; + } + + pa_memchunk_reset(&m->current); + + return 0; + } + + /* There's simply nothing */ + return -1; + +} diff --git a/src/polypcore/mcalign.h b/src/polypcore/mcalign.h new file mode 100644 index 00000000..5de75bc7 --- /dev/null +++ b/src/polypcore/mcalign.h @@ -0,0 +1,77 @@ +#ifndef foomcalignhfoo +#define foomcalignhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "memblock.h" +#include "memchunk.h" + +/* An alignment object, used for aligning memchunks to multiples of + * the frame size. */ + +/* Method of operation: the user creates a new mcalign object by + * calling pa_mcalign_new() with the appropriate aligning + * granularity. After that he may call pa_mcalign_push() for an input + * memchunk. After exactly one memchunk the user has to call + * pa_mcalign_pop() until it returns -1. If pa_mcalign_pop() returns + * 0, the memchunk *c is valid and aligned to the granularity. Some + * pseudocode illustrating this: + * + * pa_mcalign *a = pa_mcalign_new(4, NULL); + * + * for (;;) { + * pa_memchunk input; + * + * ... fill input ... + * + * pa_mcalign_push(m, &input); + * pa_memblock_unref(input.memblock); + * + * for (;;) { + * pa_memchunk output; + * + * if (pa_mcalign_pop(m, &output) < 0) + * break; + * + * ... consume output ... + * + * pa_memblock_unref(output.memblock); + * } + * } + * + * pa_memchunk_free(a); + * */ + +typedef struct pa_mcalign pa_mcalign; + +pa_mcalign *pa_mcalign_new(size_t base, pa_memblock_stat *s); +void pa_mcalign_free(pa_mcalign *m); + +/* Push a new memchunk into the aligner. The caller of this routine + * has to free the memchunk by himself. */ +void pa_mcalign_push(pa_mcalign *m, const pa_memchunk *c); + +/* Pop a new memchunk from the aligner. Returns 0 when sucessful, + * nonzero otherwise. */ +int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c); + +#endif diff --git a/src/polypcore/memblock.c b/src/polypcore/memblock.c new file mode 100644 index 00000000..8da53525 --- /dev/null +++ b/src/polypcore/memblock.c @@ -0,0 +1,169 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "memblock.h" +#include "xmalloc.h" + +static void stat_add(pa_memblock*m, pa_memblock_stat *s) { + assert(m); + + if (!s) { + m->stat = NULL; + return; + } + + m->stat = pa_memblock_stat_ref(s); + s->total++; + s->allocated++; + s->total_size += m->length; + s->allocated_size += m->length; +} + +static void stat_remove(pa_memblock *m) { + assert(m); + + if (!m->stat) + return; + + m->stat->total--; + m->stat->total_size -= m->length; + + pa_memblock_stat_unref(m->stat); + m->stat = NULL; +} + +pa_memblock *pa_memblock_new(size_t length, pa_memblock_stat*s) { + pa_memblock *b = pa_xmalloc(sizeof(pa_memblock)+length); + b->type = PA_MEMBLOCK_APPENDED; + b->ref = 1; + b->length = length; + b->data = b+1; + b->free_cb = NULL; + b->read_only = 0; + stat_add(b, s); + return b; +} + +pa_memblock *pa_memblock_new_dynamic(void *d, size_t length, pa_memblock_stat*s) { + pa_memblock *b = pa_xmalloc(sizeof(pa_memblock)); + b->type = PA_MEMBLOCK_DYNAMIC; + b->ref = 1; + b->length = length; + b->data = d; + b->free_cb = NULL; + b->read_only = 0; + stat_add(b, s); + return b; +} + +pa_memblock *pa_memblock_new_fixed(void *d, size_t length, int read_only, pa_memblock_stat*s) { + pa_memblock *b = pa_xmalloc(sizeof(pa_memblock)); + b->type = PA_MEMBLOCK_FIXED; + b->ref = 1; + b->length = length; + b->data = d; + b->free_cb = NULL; + b->read_only = read_only; + stat_add(b, s); + return b; +} + +pa_memblock *pa_memblock_new_user(void *d, size_t length, void (*free_cb)(void *p), int read_only, pa_memblock_stat*s) { + pa_memblock *b; + assert(d && length && free_cb); + b = pa_xmalloc(sizeof(pa_memblock)); + b->type = PA_MEMBLOCK_USER; + b->ref = 1; + b->length = length; + b->data = d; + b->free_cb = free_cb; + b->read_only = read_only; + stat_add(b, s); + return b; +} + +pa_memblock* pa_memblock_ref(pa_memblock*b) { + assert(b && b->ref >= 1); + b->ref++; + return b; +} + +void pa_memblock_unref(pa_memblock*b) { + assert(b && b->ref >= 1); + + if ((--(b->ref)) == 0) { + stat_remove(b); + + if (b->type == PA_MEMBLOCK_USER) { + assert(b->free_cb); + b->free_cb(b->data); + } else if (b->type == PA_MEMBLOCK_DYNAMIC) + pa_xfree(b->data); + + pa_xfree(b); + } +} + +void pa_memblock_unref_fixed(pa_memblock *b) { + assert(b && b->ref >= 1 && b->type == PA_MEMBLOCK_FIXED); + + if (b->ref == 1) + pa_memblock_unref(b); + else { + b->data = pa_xmemdup(b->data, b->length); + b->type = PA_MEMBLOCK_DYNAMIC; + b->ref--; + } +} + +pa_memblock_stat* pa_memblock_stat_new(void) { + pa_memblock_stat *s; + + s = pa_xmalloc(sizeof(pa_memblock_stat)); + s->ref = 1; + s->total = s->total_size = s->allocated = s->allocated_size = 0; + + return s; +} + +void pa_memblock_stat_unref(pa_memblock_stat *s) { + assert(s && s->ref >= 1); + + if (!(--(s->ref))) { + assert(!s->total); + pa_xfree(s); + } +} + +pa_memblock_stat * pa_memblock_stat_ref(pa_memblock_stat *s) { + assert(s); + s->ref++; + return s; +} diff --git a/src/polypcore/memblock.h b/src/polypcore/memblock.h new file mode 100644 index 00000000..c5751406 --- /dev/null +++ b/src/polypcore/memblock.h @@ -0,0 +1,87 @@ +#ifndef foomemblockhfoo +#define foomemblockhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +/* A pa_memblock is a reference counted memory block. Polypaudio + * passed references to pa_memblocks around instead of copying + * data. See pa_memchunk for a structure that describes parts of + * memory blocks. */ + +/* The type of memory this block points to */ +typedef enum pa_memblock_type { + PA_MEMBLOCK_FIXED, /* data is a pointer to fixed memory that needs not to be freed */ + PA_MEMBLOCK_APPENDED, /* The most common kind: the data is appended to the memory block */ + PA_MEMBLOCK_DYNAMIC, /* data is a pointer to some memory allocated with pa_xmalloc() */ + PA_MEMBLOCK_USER /* User supplied memory, to be freed with free_cb */ +} pa_memblock_type_t; + +/* A structure of keeping memory block statistics */ +/* Maintains statistics about memory blocks */ +typedef struct pa_memblock_stat { + int ref; + unsigned total; + unsigned total_size; + unsigned allocated; + unsigned allocated_size; +} pa_memblock_stat; + +typedef struct pa_memblock { + pa_memblock_type_t type; + unsigned ref; /* the reference counter */ + int read_only; /* boolean */ + size_t length; + void *data; + void (*free_cb)(void *p); /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */ + pa_memblock_stat *stat; +} pa_memblock; + +/* Allocate a new memory block of type PA_MEMBLOCK_APPENDED */ +pa_memblock *pa_memblock_new(size_t length, pa_memblock_stat*s); + +/* Allocate a new memory block of type PA_MEMBLOCK_DYNAMIC. The pointer data is to be maintained be the memory block */ +pa_memblock *pa_memblock_new_dynamic(void *data, size_t length, pa_memblock_stat*s); + +/* Allocate a new memory block of type PA_MEMBLOCK_FIXED */ +pa_memblock *pa_memblock_new_fixed(void *data, size_t length, int read_only, pa_memblock_stat*s); + +/* Allocate a new memory block of type PA_MEMBLOCK_USER */ +pa_memblock *pa_memblock_new_user(void *data, size_t length, void (*free_cb)(void *p), int read_only, pa_memblock_stat*s); + +void pa_memblock_unref(pa_memblock*b); +pa_memblock* pa_memblock_ref(pa_memblock*b); + +/* This special unref function has to be called by the owner of the +memory of a static memory block when he wants to release all +references to the memory. This causes the memory to be copied and +converted into a PA_MEMBLOCK_DYNAMIC type memory block */ +void pa_memblock_unref_fixed(pa_memblock*b); + + +pa_memblock_stat* pa_memblock_stat_new(void); +void pa_memblock_stat_unref(pa_memblock_stat *s); +pa_memblock_stat * pa_memblock_stat_ref(pa_memblock_stat *s); + +#endif diff --git a/src/polypcore/memblockq.c b/src/polypcore/memblockq.c new file mode 100644 index 00000000..ba6b76ea --- /dev/null +++ b/src/polypcore/memblockq.c @@ -0,0 +1,343 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "memblockq.h" +#include "xmalloc.h" +#include "log.h" +#include "mcalign.h" + +struct memblock_list { + struct memblock_list *next, *prev; + pa_memchunk chunk; +}; + +struct pa_memblockq { + struct memblock_list *blocks, *blocks_tail; + unsigned n_blocks; + size_t current_length, maxlength, tlength, base, prebuf, orig_prebuf, minreq; + pa_mcalign *mcalign; + pa_memblock_stat *memblock_stat; +}; + +pa_memblockq* pa_memblockq_new(size_t maxlength, size_t tlength, size_t base, size_t prebuf, size_t minreq, pa_memblock_stat *s) { + pa_memblockq* bq; + assert(maxlength && base && maxlength); + + bq = pa_xmalloc(sizeof(pa_memblockq)); + bq->blocks = bq->blocks_tail = 0; + bq->n_blocks = 0; + + bq->current_length = 0; + + pa_log_debug(__FILE__": memblockq requested: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", maxlength, tlength, base, prebuf, minreq); + + bq->base = base; + + bq->maxlength = ((maxlength+base-1)/base)*base; + assert(bq->maxlength >= base); + + bq->tlength = ((tlength+base-1)/base)*base; + if (!bq->tlength || bq->tlength >= bq->maxlength) + bq->tlength = bq->maxlength; + + bq->minreq = (minreq/base)*base; + if (bq->minreq == 0) + bq->minreq = 1; + + bq->prebuf = (prebuf == (size_t) -1) ? bq->maxlength/2 : prebuf; + bq->prebuf = (bq->prebuf/base)*base; + if (bq->prebuf > bq->maxlength) + bq->prebuf = bq->maxlength; + + if (bq->prebuf > bq->tlength - bq->minreq) + bq->prebuf = bq->tlength - bq->minreq; + + bq->orig_prebuf = bq->prebuf; + + pa_log_debug(__FILE__": memblockq sanitized: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", bq->maxlength, bq->tlength, bq->base, bq->prebuf, bq->minreq); + + bq->mcalign = NULL; + + bq->memblock_stat = s; + + return bq; +} + +void pa_memblockq_free(pa_memblockq* bq) { + assert(bq); + + pa_memblockq_flush(bq); + + if (bq->mcalign) + pa_mcalign_free(bq->mcalign); + + pa_xfree(bq); +} + +void pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta) { + struct memblock_list *q; + assert(bq && chunk && chunk->memblock && chunk->length && (chunk->length % bq->base) == 0); + + pa_memblockq_seek(bq, delta); + + if (bq->blocks_tail && bq->blocks_tail->chunk.memblock == chunk->memblock) { + /* Try to merge memory chunks */ + + if (bq->blocks_tail->chunk.index+bq->blocks_tail->chunk.length == chunk->index) { + bq->blocks_tail->chunk.length += chunk->length; + bq->current_length += chunk->length; + return; + } + } + + q = pa_xmalloc(sizeof(struct memblock_list)); + + q->chunk = *chunk; + pa_memblock_ref(q->chunk.memblock); + assert(q->chunk.index+q->chunk.length <= q->chunk.memblock->length); + q->next = NULL; + if ((q->prev = bq->blocks_tail)) + bq->blocks_tail->next = q; + else + bq->blocks = q; + + bq->blocks_tail = q; + + bq->n_blocks++; + bq->current_length += chunk->length; + + pa_memblockq_shorten(bq, bq->maxlength); +} + +int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) { + assert(bq && chunk); + + if (!bq->blocks || bq->current_length < bq->prebuf) + return -1; + + bq->prebuf = 0; + + *chunk = bq->blocks->chunk; + pa_memblock_ref(chunk->memblock); + + return 0; +} + +void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length) { + assert(bq && chunk && length); + + if (!bq->blocks || memcmp(&bq->blocks->chunk, chunk, sizeof(pa_memchunk))) + return; + + assert(length <= bq->blocks->chunk.length); + pa_memblockq_skip(bq, length); +} + +static void remove_block(pa_memblockq *bq, struct memblock_list *q) { + assert(bq && q); + + if (q->prev) + q->prev->next = q->next; + else { + assert(bq->blocks == q); + bq->blocks = q->next; + } + + if (q->next) + q->next->prev = q->prev; + else { + assert(bq->blocks_tail == q); + bq->blocks_tail = q->prev; + } + + pa_memblock_unref(q->chunk.memblock); + pa_xfree(q); + + bq->n_blocks--; +} + +void pa_memblockq_skip(pa_memblockq *bq, size_t length) { + assert(bq && length && (length % bq->base) == 0); + + while (length > 0) { + size_t l = length; + assert(bq->blocks && bq->current_length >= length); + + if (l > bq->blocks->chunk.length) + l = bq->blocks->chunk.length; + + bq->blocks->chunk.index += l; + bq->blocks->chunk.length -= l; + bq->current_length -= l; + + if (!bq->blocks->chunk.length) + remove_block(bq, bq->blocks); + + length -= l; + } +} + +void pa_memblockq_shorten(pa_memblockq *bq, size_t length) { + size_t l; + assert(bq); + + if (bq->current_length <= length) + return; + + /*pa_log(__FILE__": Warning! pa_memblockq_shorten()\n");*/ + + l = bq->current_length - length; + l /= bq->base; + l *= bq->base; + + pa_memblockq_skip(bq, l); +} + + +void pa_memblockq_empty(pa_memblockq *bq) { + assert(bq); + pa_memblockq_shorten(bq, 0); +} + +int pa_memblockq_is_readable(pa_memblockq *bq) { + assert(bq); + + return bq->current_length && (bq->current_length >= bq->prebuf); +} + +int pa_memblockq_is_writable(pa_memblockq *bq, size_t length) { + assert(bq); + + return bq->current_length + length <= bq->tlength; +} + +uint32_t pa_memblockq_get_length(pa_memblockq *bq) { + assert(bq); + return bq->current_length; +} + +uint32_t pa_memblockq_missing(pa_memblockq *bq) { + size_t l; + assert(bq); + + if (bq->current_length >= bq->tlength) + return 0; + + l = bq->tlength - bq->current_length; + assert(l); + + return (l >= bq->minreq) ? l : 0; +} + +void pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta) { + pa_memchunk rchunk; + assert(bq && chunk && bq->base); + + if (bq->base == 1) { + pa_memblockq_push(bq, chunk, delta); + return; + } + + if (!bq->mcalign) { + bq->mcalign = pa_mcalign_new(bq->base, bq->memblock_stat); + assert(bq->mcalign); + } + + pa_mcalign_push(bq->mcalign, chunk); + + while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) { + pa_memblockq_push(bq, &rchunk, delta); + pa_memblock_unref(rchunk.memblock); + delta = 0; + } +} + +uint32_t pa_memblockq_get_minreq(pa_memblockq *bq) { + assert(bq); + return bq->minreq; +} + +void pa_memblockq_prebuf_disable(pa_memblockq *bq) { + assert(bq); + bq->prebuf = 0; +} + +void pa_memblockq_prebuf_reenable(pa_memblockq *bq) { + assert(bq); + bq->prebuf = bq->orig_prebuf; +} + +void pa_memblockq_seek(pa_memblockq *bq, size_t length) { + assert(bq); + + if (!length) + return; + + while (length >= bq->base) { + size_t l = length; + if (!bq->current_length) + return; + + assert(bq->blocks_tail); + + if (l > bq->blocks_tail->chunk.length) + l = bq->blocks_tail->chunk.length; + + bq->blocks_tail->chunk.length -= l; + bq->current_length -= l; + + if (bq->blocks_tail->chunk.length == 0) + remove_block(bq, bq->blocks); + + length -= l; + } +} + +void pa_memblockq_flush(pa_memblockq *bq) { + struct memblock_list *l; + assert(bq); + + while ((l = bq->blocks)) { + bq->blocks = l->next; + pa_memblock_unref(l->chunk.memblock); + pa_xfree(l); + } + + bq->blocks_tail = NULL; + bq->n_blocks = 0; + bq->current_length = 0; +} + +uint32_t pa_memblockq_get_tlength(pa_memblockq *bq) { + assert(bq); + return bq->tlength; +} diff --git a/src/polypcore/memblockq.h b/src/polypcore/memblockq.h new file mode 100644 index 00000000..1695daba --- /dev/null +++ b/src/polypcore/memblockq.h @@ -0,0 +1,104 @@ +#ifndef foomemblockqhfoo +#define foomemblockqhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "memblock.h" +#include "memchunk.h" + +/* A memblockq is a queue of pa_memchunks (yepp, the name is not + * perfect). It is similar to the ring buffers used by most other + * audio software. In contrast to a ring buffer this memblockq data + * type doesn't need to copy any data around, it just maintains + * references to reference counted memory blocks. */ + +typedef struct pa_memblockq pa_memblockq; + +/* Parameters: + - maxlength: maximum length of queue. If more data is pushed into the queue, data from the front is dropped + - length: the target length of the queue. + - base: a base value for all metrics. Only multiples of this value are popped from the queue + - prebuf: before passing the first byte out, make sure that enough bytes are in the queue + - minreq: pa_memblockq_missing() will only return values greater than this value +*/ +pa_memblockq* pa_memblockq_new(size_t maxlength, + size_t tlength, + size_t base, + size_t prebuf, + size_t minreq, + pa_memblock_stat *s); +void pa_memblockq_free(pa_memblockq*bq); + +/* Push a new memory chunk into the queue. Optionally specify a value for future cancellation. */ +void pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta); + +/* Same as pa_memblockq_push(), however chunks are filtered through a mcalign object, and thus aligned to multiples of base */ +void pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk, size_t delta); + +/* Return a copy of the next memory chunk in the queue. It is not removed from the queue */ +int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk); + +/* Drop the specified bytes from the queue, only valid aufter pa_memblockq_peek() */ +void pa_memblockq_drop(pa_memblockq *bq, const pa_memchunk *chunk, size_t length); + +/* Drop the specified bytes from the queue */ +void pa_memblockq_skip(pa_memblockq *bq, size_t length); + +/* Shorten the pa_memblockq to the specified length by dropping data at the end of the queue */ +void pa_memblockq_shorten(pa_memblockq *bq, size_t length); + +/* Empty the pa_memblockq */ +void pa_memblockq_empty(pa_memblockq *bq); + +/* Test if the pa_memblockq is currently readable, that is, more data than base */ +int pa_memblockq_is_readable(pa_memblockq *bq); + +/* Test if the pa_memblockq is currently writable for the specified amount of bytes */ +int pa_memblockq_is_writable(pa_memblockq *bq, size_t length); + +/* Return the length of the queue in bytes */ +uint32_t pa_memblockq_get_length(pa_memblockq *bq); + +/* Return how many bytes are missing in queue to the specified fill amount */ +uint32_t pa_memblockq_missing(pa_memblockq *bq); + +/* Returns the minimal request value */ +uint32_t pa_memblockq_get_minreq(pa_memblockq *bq); + +/* Force disabling of pre-buf even when the pre-buffer is not yet filled */ +void pa_memblockq_prebuf_disable(pa_memblockq *bq); + +/* Reenable pre-buf to the initial level */ +void pa_memblockq_prebuf_reenable(pa_memblockq *bq); + +/* Manipulate the write pointer */ +void pa_memblockq_seek(pa_memblockq *bq, size_t delta); + +/* Flush the queue */ +void pa_memblockq_flush(pa_memblockq *bq); + +/* Get Target length */ +uint32_t pa_memblockq_get_tlength(pa_memblockq *bq); + +#endif diff --git a/src/polypcore/memchunk.c b/src/polypcore/memchunk.c new file mode 100644 index 00000000..bfd74f9e --- /dev/null +++ b/src/polypcore/memchunk.c @@ -0,0 +1,58 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "memchunk.h" +#include "xmalloc.h" + +void pa_memchunk_make_writable(pa_memchunk *c, pa_memblock_stat *s, size_t min) { + pa_memblock *n; + size_t l; + assert(c && c->memblock && c->memblock->ref >= 1); + + if (c->memblock->ref == 1 && !c->memblock->read_only && c->memblock->length >= c->index+min) + return; + + l = c->length; + if (l < min) + l = min; + + n = pa_memblock_new(l, s); + memcpy(n->data, (uint8_t*) c->memblock->data + c->index, c->length); + pa_memblock_unref(c->memblock); + c->memblock = n; + c->index = 0; +} + +void pa_memchunk_reset(pa_memchunk *c) { + assert(c); + + c->memblock = NULL; + c->length = c->index = 0; +} diff --git a/src/polypcore/memchunk.h b/src/polypcore/memchunk.h new file mode 100644 index 00000000..4eefc8c1 --- /dev/null +++ b/src/polypcore/memchunk.h @@ -0,0 +1,45 @@ +#ifndef foomemchunkhfoo +#define foomemchunkhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "memblock.h" + +/* A memchunk describes a part of a memblock. In contrast to the memblock, a + * memchunk is not allocated dynamically or reference counted, instead + * it is usually stored on the stack and copied around */ + +typedef struct pa_memchunk { + pa_memblock *memblock; + size_t index, length; +} pa_memchunk; + +/* Make a memchunk writable, i.e. make sure that the caller may have + * exclusive access to the memblock and it is not read_only. If needed + * the memblock in the structure is replaced by a copy. */ +void pa_memchunk_make_writable(pa_memchunk *c, pa_memblock_stat *s, size_t min); + +/* Invalidate a memchunk. This does not free the cotaining memblock, + * but sets all members to zero. */ +void pa_memchunk_reset(pa_memchunk *c); + +#endif diff --git a/src/polypcore/modargs.c b/src/polypcore/modargs.c new file mode 100644 index 00000000..07062946 --- /dev/null +++ b/src/polypcore/modargs.c @@ -0,0 +1,260 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "hashmap.h" +#include "modargs.h" +#include "idxset.h" +#include "sample-util.h" +#include "namereg.h" +#include "sink.h" +#include "source.h" +#include "xmalloc.h" +#include "util.h" + +struct entry { + char *key, *value; +}; + +static int add_key_value(pa_hashmap *map, char *key, char *value, const char* const valid_keys[]) { + struct entry *e; + assert(map && key && value); + + if (valid_keys) { + const char*const* v; + for (v = valid_keys; *v; v++) + if (strcmp(*v, key) == 0) + break; + + if (!*v) { + pa_xfree(key); + pa_xfree(value); + return -1; + } + } + + e = pa_xmalloc(sizeof(struct entry)); + e->key = key; + e->value = value; + pa_hashmap_put(map, key, e); + return 0; +} + +pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) { + pa_hashmap *map = NULL; + + map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + assert(map); + + if (args) { + enum { WHITESPACE, KEY, VALUE_START, VALUE_SIMPLE, VALUE_DOUBLE_QUOTES, VALUE_TICKS } state; + const char *p, *key, *value; + size_t key_len = 0, value_len = 0; + + key = value = NULL; + state = WHITESPACE; + for (p = args; *p; p++) { + switch (state) { + case WHITESPACE: + if (*p == '=') + goto fail; + else if (!isspace(*p)) { + key = p; + state = KEY; + key_len = 1; + } + break; + case KEY: + if (*p == '=') + state = VALUE_START; + else + key_len++; + break; + case VALUE_START: + if (*p == '\'') { + state = VALUE_TICKS; + value = p+1; + value_len = 0; + } else if (*p == '"') { + state = VALUE_DOUBLE_QUOTES; + value = p+1; + value_len = 0; + } else if (isspace(*p)) { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0) + goto fail; + state = WHITESPACE; + } else { + state = VALUE_SIMPLE; + value = p; + value_len = 1; + } + break; + case VALUE_SIMPLE: + if (isspace(*p)) { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) + goto fail; + state = WHITESPACE; + } else + value_len++; + break; + case VALUE_DOUBLE_QUOTES: + if (*p == '"') { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) + goto fail; + state = WHITESPACE; + } else + value_len++; + break; + case VALUE_TICKS: + if (*p == '\'') { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) + goto fail; + state = WHITESPACE; + } else + value_len++; + break; + } + } + + if (state == VALUE_START) { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0) + goto fail; + } else if (state == VALUE_SIMPLE) { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(value), valid_keys) < 0) + goto fail; + } else if (state != WHITESPACE) + goto fail; + } + + return (pa_modargs*) map; + +fail: + + if (map) + pa_modargs_free((pa_modargs*) map); + + return NULL; +} + + +static void free_func(void *p, PA_GCC_UNUSED void*userdata) { + struct entry *e = p; + assert(e); + pa_xfree(e->key); + pa_xfree(e->value); + pa_xfree(e); +} + +void pa_modargs_free(pa_modargs*ma) { + pa_hashmap *map = (pa_hashmap*) ma; + pa_hashmap_free(map, free_func, NULL); +} + +const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *def) { + pa_hashmap *map = (pa_hashmap*) ma; + struct entry*e; + + if (!(e = pa_hashmap_get(map, key))) + return def; + + return e->value; +} + +int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value) { + const char *v; + assert(ma && key && value); + + if (!(v = pa_modargs_get_value(ma, key, NULL))) + return 0; + + if (pa_atou(v, value) < 0) + return -1; + + return 0; +} + +int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value) { + const char *v; + assert(ma && key && value); + + if (!(v = pa_modargs_get_value(ma, key, NULL))) + return 0; + + if (pa_atoi(v, value) < 0) + return -1; + + return 0; +} + +int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value) { + const char *v; + int r; + assert(ma && key && value); + + if (!(v = pa_modargs_get_value(ma, key, NULL))) + return 0; + + if (!*v) + return -1; + + if ((r = pa_parse_boolean(v)) < 0) + return -1; + + *value = r; + return 0; +} + +int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *rss) { + const char *format; + uint32_t channels; + pa_sample_spec ss; + assert(ma && rss); + +/* DEBUG_TRAP;*/ + + ss = *rss; + if ((pa_modargs_get_value_u32(ma, "rate", &ss.rate)) < 0) + return -1; + + channels = ss.channels; + if ((pa_modargs_get_value_u32(ma, "channels", &channels)) < 0) + return -1; + ss.channels = (uint8_t) channels; + + if ((format = pa_modargs_get_value(ma, "format", NULL))) + if ((ss.format = pa_parse_sample_format(format)) < 0) + return -1; + + if (!pa_sample_spec_valid(&ss)) + return -1; + + *rss = ss; + + return 0; +} diff --git a/src/polypcore/modargs.h b/src/polypcore/modargs.h new file mode 100644 index 00000000..678ea1a7 --- /dev/null +++ b/src/polypcore/modargs.h @@ -0,0 +1,49 @@ +#ifndef foomodargshfoo +#define foomodargshfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include "core.h" + +typedef struct pa_modargs pa_modargs; + +/* A generic parser for module arguments */ + +/* Parse the string args. The NULL-terminated array keys contains all valid arguments. */ +pa_modargs *pa_modargs_new(const char *args, const char* const keys[]); +void pa_modargs_free(pa_modargs*ma); + +/* Return the module argument for the specified name as a string. If + * the argument was not specified, return def instead.*/ +const char *pa_modargs_get_value(pa_modargs *ma, const char *key, const char *def); + +/* Return a module argument as unsigned 32bit value in *value */ +int pa_modargs_get_value_u32(pa_modargs *ma, const char *key, uint32_t *value); +int pa_modargs_get_value_s32(pa_modargs *ma, const char *key, int32_t *value); +int pa_modargs_get_value_boolean(pa_modargs *ma, const char *key, int *value); + +/* Return sample spec data from the three arguments "rate", "format" and "channels" */ +int pa_modargs_get_sample_spec(pa_modargs *ma, pa_sample_spec *ss); + +#endif diff --git a/src/polypcore/modinfo.c b/src/polypcore/modinfo.c new file mode 100644 index 00000000..53440612 --- /dev/null +++ b/src/polypcore/modinfo.c @@ -0,0 +1,84 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "xmalloc.h" +#include "util.h" +#include "modinfo.h" +#include "log.h" + +#define PA_SYMBOL_AUTHOR "pa__get_author" +#define PA_SYMBOL_DESCRIPTION "pa__get_description" +#define PA_SYMBOL_USAGE "pa__get_usage" +#define PA_SYMBOL_VERSION "pa__get_version" + +pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl) { + pa_modinfo *i; + const char* (*func)(void); + assert(dl); + + i = pa_xmalloc0(sizeof(pa_modinfo)); + + if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_AUTHOR))) + i->author = pa_xstrdup(func()); + + if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_DESCRIPTION))) + i->description = pa_xstrdup(func()); + + if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_USAGE))) + i->usage = pa_xstrdup(func()); + + if ((func = (const char* (*)(void)) lt_dlsym(dl, PA_SYMBOL_VERSION))) + i->version = pa_xstrdup(func()); + + return i; +} + +pa_modinfo *pa_modinfo_get_by_name(const char *name) { + lt_dlhandle dl; + pa_modinfo *i; + assert(name); + + if (!(dl = lt_dlopenext(name))) { + pa_log(__FILE__": Failed to open module \"%s\": %s\n", name, lt_dlerror()); + return NULL; + } + + i = pa_modinfo_get_by_handle(dl); + lt_dlclose(dl); + + return i; +} + +void pa_modinfo_free(pa_modinfo *i) { + assert(i); + pa_xfree(i->author); + pa_xfree(i->description); + pa_xfree(i->usage); + pa_xfree(i->version); + pa_xfree(i); +} diff --git a/src/polypcore/modinfo.h b/src/polypcore/modinfo.h new file mode 100644 index 00000000..53176147 --- /dev/null +++ b/src/polypcore/modinfo.h @@ -0,0 +1,43 @@ +#ifndef foomodinfohfoo +#define foomodinfohfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* Some functions for reading module meta data from Polypaudio modules */ + +typedef struct pa_modinfo { + char *author; + char *description; + char *usage; + char *version; +} pa_modinfo; + +/* Read meta data from an libtool handle */ +pa_modinfo *pa_modinfo_get_by_handle(lt_dlhandle dl); + +/* Read meta data from a module file */ +pa_modinfo *pa_modinfo_get_by_name(const char *name); + +/* Free meta data */ +void pa_modinfo_free(pa_modinfo *i); + +#endif diff --git a/src/polypcore/module.c b/src/polypcore/module.c new file mode 100644 index 00000000..499ea299 --- /dev/null +++ b/src/polypcore/module.c @@ -0,0 +1,275 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "module.h" +#include "xmalloc.h" +#include "subscribe.h" +#include "log.h" +#include "util.h" + +#define PA_SYMBOL_INIT "pa__init" +#define PA_SYMBOL_DONE "pa__done" + +#define UNLOAD_POLL_TIME 2 + +static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { + pa_core *c = userdata; + struct timeval ntv; + assert(c && c->mainloop == m && c->module_auto_unload_event == e); + + pa_module_unload_unused(c); + + pa_gettimeofday(&ntv); + ntv.tv_sec += UNLOAD_POLL_TIME; + m->time_restart(e, &ntv); +} + +pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { + pa_module *m = NULL; + int r; + + assert(c && name); + + if (c->disallow_module_loading) + goto fail; + + m = pa_xmalloc(sizeof(pa_module)); + + m->name = pa_xstrdup(name); + m->argument = pa_xstrdup(argument); + + if (!(m->dl = lt_dlopenext(name))) { + pa_log(__FILE__": Failed to open module \"%s\": %s\n", name, lt_dlerror()); + goto fail; + } + + if (!(m->init = (int (*)(pa_core *_c, pa_module*_m)) lt_dlsym(m->dl, PA_SYMBOL_INIT))) { + pa_log(__FILE__": Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.\n", name); + goto fail; + } + + if (!(m->done = (void (*)(pa_core *_c, pa_module*_m)) lt_dlsym(m->dl, PA_SYMBOL_DONE))) { + pa_log(__FILE__": Failed to load module \"%s\": symbol \""PA_SYMBOL_DONE"\" not found.\n", name); + goto fail; + } + + m->userdata = NULL; + m->core = c; + m->n_used = -1; + m->auto_unload = 0; + m->unload_requested = 0; + + assert(m->init); + if (m->init(c, m) < 0) { + pa_log_error(__FILE__": Failed to load module \"%s\" (argument: \"%s\"): initialization failed.\n", name, argument ? argument : ""); + goto fail; + } + + if (!c->modules) + c->modules = pa_idxset_new(NULL, NULL); + + if (!c->module_auto_unload_event) { + struct timeval ntv; + pa_gettimeofday(&ntv); + ntv.tv_sec += UNLOAD_POLL_TIME; + c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c); + } + assert(c->module_auto_unload_event); + + assert(c->modules); + r = pa_idxset_put(c->modules, m, &m->index); + assert(r >= 0 && m->index != PA_IDXSET_INVALID); + + pa_log_info(__FILE__": Loaded \"%s\" (index: #%u; argument: \"%s\").\n", m->name, m->index, m->argument ? m->argument : ""); + + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index); + + return m; + +fail: + + if (m) { + pa_xfree(m->argument); + pa_xfree(m->name); + + if (m->dl) + lt_dlclose(m->dl); + + pa_xfree(m); + } + + return NULL; +} + +static void pa_module_free(pa_module *m) { + assert(m && m->done && m->core); + + if (m->core->disallow_module_loading) + return; + + pa_log_info(__FILE__": Unloading \"%s\" (index: #%u).\n", m->name, m->index); + + m->done(m->core, m); + + lt_dlclose(m->dl); + + pa_log_info(__FILE__": Unloaded \"%s\" (index: #%u).\n", m->name, m->index); + + pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_REMOVE, m->index); + + pa_xfree(m->name); + pa_xfree(m->argument); + pa_xfree(m); +} + +void pa_module_unload(pa_core *c, pa_module *m) { + assert(c && m); + + assert(c->modules); + if (!(m = pa_idxset_remove_by_data(c->modules, m, NULL))) + return; + + pa_module_free(m); +} + +void pa_module_unload_by_index(pa_core *c, uint32_t idx) { + pa_module *m; + assert(c && idx != PA_IDXSET_INVALID); + + assert(c->modules); + if (!(m = pa_idxset_remove_by_index(c->modules, idx))) + return; + + pa_module_free(m); +} + +static void free_callback(void *p, PA_GCC_UNUSED void *userdata) { + pa_module *m = p; + assert(m); + pa_module_free(m); +} + +void pa_module_unload_all(pa_core *c) { + assert(c); + + if (!c->modules) + return; + + pa_idxset_free(c->modules, free_callback, NULL); + c->modules = NULL; + + if (c->module_auto_unload_event) { + c->mainloop->time_free(c->module_auto_unload_event); + c->module_auto_unload_event = NULL; + } + + if (c->module_defer_unload_event) { + c->mainloop->defer_free(c->module_defer_unload_event); + c->module_defer_unload_event = NULL; + } +} + +static int unused_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void *userdata) { + pa_module *m = p; + time_t *now = userdata; + assert(p && del && now); + + if (m->n_used == 0 && m->auto_unload && m->last_used_time+m->core->module_idle_time <= *now) { + pa_module_free(m); + *del = 1; + } + + return 0; +} + +void pa_module_unload_unused(pa_core *c) { + time_t now; + assert(c); + + if (!c->modules) + return; + + time(&now); + pa_idxset_foreach(c->modules, unused_callback, &now); +} + +static int unload_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, PA_GCC_UNUSED void *userdata) { + pa_module *m = p; + assert(m); + + if (m->unload_requested) { + pa_module_free(m); + *del = 1; + } + + return 0; +} + +static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) { + pa_core *core = userdata; + api->defer_enable(e, 0); + + if (!core->modules) + return; + + pa_idxset_foreach(core->modules, unload_callback, NULL); + +} + +void pa_module_unload_request(pa_module *m) { + assert(m); + + m->unload_requested = 1; + + if (!m->core->module_defer_unload_event) + m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core); + + m->core->mainloop->defer_enable(m->core->module_defer_unload_event, 1); +} + +void pa_module_set_used(pa_module*m, int used) { + assert(m); + + if (m->n_used != used) + pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index); + + if (m->n_used != used && used == 0) + time(&m->last_used_time); + + m->n_used = used; +} + +pa_modinfo *pa_module_get_info(pa_module *m) { + assert(m); + + return pa_modinfo_get_by_handle(m->dl); +} diff --git a/src/polypcore/module.h b/src/polypcore/module.h new file mode 100644 index 00000000..6f137c15 --- /dev/null +++ b/src/polypcore/module.h @@ -0,0 +1,70 @@ +#ifndef foomodulehfoo +#define foomodulehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +#include "core.h" +#include "modinfo.h" + +typedef struct pa_module pa_module; + +struct pa_module { + pa_core *core; + char *name, *argument; + uint32_t index; + + lt_dlhandle dl; + + int (*init)(pa_core *c, pa_module*m); + void (*done)(pa_core *c, pa_module*m); + + void *userdata; + + int n_used; + int auto_unload; + time_t last_used_time; + + int unload_requested; +}; + +pa_module* pa_module_load(pa_core *c, const char *name, const char*argument); +void pa_module_unload(pa_core *c, pa_module *m); +void pa_module_unload_by_index(pa_core *c, uint32_t idx); + +void pa_module_unload_all(pa_core *c); +void pa_module_unload_unused(pa_core *c); + +void pa_module_unload_request(pa_module *m); + +void pa_module_set_used(pa_module*m, int used); + +#define PA_MODULE_AUTHOR(s) const char * pa__get_author(void) { return s; } +#define PA_MODULE_DESCRIPTION(s) const char * pa__get_description(void) { return s; } +#define PA_MODULE_USAGE(s) const char * pa__get_usage(void) { return s; } +#define PA_MODULE_VERSION(s) const char * pa__get_version(void) { return s; } + +pa_modinfo *pa_module_get_info(pa_module *m); + +#endif diff --git a/src/polypcore/namereg.c b/src/polypcore/namereg.c new file mode 100644 index 00000000..07fb485c --- /dev/null +++ b/src/polypcore/namereg.c @@ -0,0 +1,212 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "namereg.h" +#include "autoload.h" +#include "source.h" +#include "sink.h" +#include "xmalloc.h" +#include "subscribe.h" +#include "util.h" + +struct namereg_entry { + pa_namereg_type_t type; + char *name; + void *data; +}; + +void pa_namereg_free(pa_core *c) { + assert(c); + if (!c->namereg) + return; + assert(pa_hashmap_size(c->namereg) == 0); + pa_hashmap_free(c->namereg, NULL, NULL); +} + +const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail) { + struct namereg_entry *e; + char *n = NULL; + int r; + + assert(c && name && data); + + if (!c->namereg) { + c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + assert(c->namereg); + } + + if ((e = pa_hashmap_get(c->namereg, name)) && fail) + return NULL; + + if (!e) + n = pa_xstrdup(name); + else { + unsigned i; + size_t l = strlen(name); + n = pa_xmalloc(l+3); + + for (i = 1; i <= 99; i++) { + snprintf(n, l+2, "%s%u", name, i); + + if (!(e = pa_hashmap_get(c->namereg, n))) + break; + } + + if (e) { + pa_xfree(n); + return NULL; + } + } + + assert(n); + e = pa_xmalloc(sizeof(struct namereg_entry)); + e->type = type; + e->name = n; + e->data = data; + + r = pa_hashmap_put(c->namereg, e->name, e); + assert (r >= 0); + + return e->name; + +} + +void pa_namereg_unregister(pa_core *c, const char *name) { + struct namereg_entry *e; + assert(c && name); + + e = pa_hashmap_remove(c->namereg, name); + assert(e); + + pa_xfree(e->name); + pa_xfree(e); +} + +void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload) { + struct namereg_entry *e; + uint32_t idx; + assert(c); + + if (!name) { + if (type == PA_NAMEREG_SOURCE) + name = pa_namereg_get_default_source_name(c); + else if (type == PA_NAMEREG_SINK) + name = pa_namereg_get_default_sink_name(c); + } + + if (!name) + return NULL; + + if (c->namereg && (e = pa_hashmap_get(c->namereg, name))) + if (e->type == e->type) + return e->data; + + if (pa_atou(name, &idx) < 0) { + + if (autoload) { + pa_autoload_request(c, name, type); + + if (c->namereg && (e = pa_hashmap_get(c->namereg, name))) + if (e->type == e->type) + return e->data; + } + + return NULL; + } + + if (type == PA_NAMEREG_SINK) + return pa_idxset_get_by_index(c->sinks, idx); + else if (type == PA_NAMEREG_SOURCE) + return pa_idxset_get_by_index(c->sources, idx); + else if (type == PA_NAMEREG_SAMPLE && c->scache) + return pa_idxset_get_by_index(c->scache, idx); + + return NULL; +} + +void pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type) { + char **s; + assert(c && (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE)); + + s = type == PA_NAMEREG_SINK ? &c->default_sink_name : &c->default_source_name; + assert(s); + + if (!name && !*s) + return; + + if (name && *s && !strcmp(name, *s)) + return; + + pa_xfree(*s); + *s = pa_xstrdup(name); + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX); +} + +const char *pa_namereg_get_default_sink_name(pa_core *c) { + pa_sink *s; + assert(c); + + if (c->default_sink_name) + return c->default_sink_name; + + if ((s = pa_idxset_first(c->sinks, NULL))) + pa_namereg_set_default(c, s->name, PA_NAMEREG_SINK); + + if (c->default_sink_name) + return c->default_sink_name; + + return NULL; +} + +const char *pa_namereg_get_default_source_name(pa_core *c) { + pa_source *s; + uint32_t idx; + + assert(c); + + if (c->default_source_name) + return c->default_source_name; + + for (s = pa_idxset_first(c->sources, &idx); s; s = pa_idxset_next(c->sources, &idx)) + if (!s->monitor_of) { + pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE); + break; + } + + if (!c->default_source_name) + if ((s = pa_idxset_first(c->sources, NULL))) + pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE); + + if (c->default_source_name) + return c->default_source_name; + + return NULL; +} diff --git a/src/polypcore/namereg.h b/src/polypcore/namereg.h new file mode 100644 index 00000000..961fd44b --- /dev/null +++ b/src/polypcore/namereg.h @@ -0,0 +1,43 @@ +#ifndef foonamereghfoo +#define foonamereghfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "core.h" + +typedef enum pa_namereg_type { + PA_NAMEREG_SINK, + PA_NAMEREG_SOURCE, + PA_NAMEREG_SAMPLE +} pa_namereg_type_t; + +void pa_namereg_free(pa_core *c); + +const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail); +void pa_namereg_unregister(pa_core *c, const char *name); +void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload); +void pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type); + +const char *pa_namereg_get_default_sink_name(pa_core *c); +const char *pa_namereg_get_default_source_name(pa_core *c); + +#endif diff --git a/src/polypcore/native-common.h b/src/polypcore/native-common.h new file mode 100644 index 00000000..78ae721e --- /dev/null +++ b/src/polypcore/native-common.h @@ -0,0 +1,114 @@ +#ifndef foonativecommonhfoo +#define foonativecommonhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +PA_C_DECL_BEGIN + +enum { + PA_COMMAND_ERROR, + PA_COMMAND_TIMEOUT, /* pseudo command */ + PA_COMMAND_REPLY, + PA_COMMAND_CREATE_PLAYBACK_STREAM, + PA_COMMAND_DELETE_PLAYBACK_STREAM, + PA_COMMAND_CREATE_RECORD_STREAM, + PA_COMMAND_DELETE_RECORD_STREAM, + PA_COMMAND_EXIT, + PA_COMMAND_REQUEST, + PA_COMMAND_AUTH, + PA_COMMAND_SET_CLIENT_NAME, + PA_COMMAND_LOOKUP_SINK, + PA_COMMAND_LOOKUP_SOURCE, + PA_COMMAND_DRAIN_PLAYBACK_STREAM, + PA_COMMAND_PLAYBACK_STREAM_KILLED, + PA_COMMAND_RECORD_STREAM_KILLED, + PA_COMMAND_STAT, + PA_COMMAND_GET_PLAYBACK_LATENCY, + PA_COMMAND_CREATE_UPLOAD_STREAM, + PA_COMMAND_DELETE_UPLOAD_STREAM, + PA_COMMAND_FINISH_UPLOAD_STREAM, + PA_COMMAND_PLAY_SAMPLE, + PA_COMMAND_REMOVE_SAMPLE, + + PA_COMMAND_GET_SERVER_INFO, + PA_COMMAND_GET_SINK_INFO, + PA_COMMAND_GET_SINK_INFO_LIST, + PA_COMMAND_GET_SOURCE_INFO, + PA_COMMAND_GET_SOURCE_INFO_LIST, + PA_COMMAND_GET_MODULE_INFO, + PA_COMMAND_GET_MODULE_INFO_LIST, + PA_COMMAND_GET_CLIENT_INFO, + PA_COMMAND_GET_CLIENT_INFO_LIST, + PA_COMMAND_GET_SINK_INPUT_INFO, + PA_COMMAND_GET_SINK_INPUT_INFO_LIST, + PA_COMMAND_GET_SOURCE_OUTPUT_INFO, + PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST, + PA_COMMAND_GET_SAMPLE_INFO, + PA_COMMAND_GET_SAMPLE_INFO_LIST, + PA_COMMAND_SUBSCRIBE, + PA_COMMAND_SUBSCRIBE_EVENT, + + PA_COMMAND_SET_SINK_VOLUME, + PA_COMMAND_SET_SINK_INPUT_VOLUME, + PA_COMMAND_CORK_PLAYBACK_STREAM, + PA_COMMAND_FLUSH_PLAYBACK_STREAM, + PA_COMMAND_TRIGGER_PLAYBACK_STREAM, + + PA_COMMAND_SET_DEFAULT_SINK, + PA_COMMAND_SET_DEFAULT_SOURCE, + + PA_COMMAND_SET_PLAYBACK_STREAM_NAME, + PA_COMMAND_SET_RECORD_STREAM_NAME, + + PA_COMMAND_KILL_CLIENT, + PA_COMMAND_KILL_SINK_INPUT, + PA_COMMAND_KILL_SOURCE_OUTPUT, + PA_COMMAND_LOAD_MODULE, + PA_COMMAND_UNLOAD_MODULE, + PA_COMMAND_ADD_AUTOLOAD, + PA_COMMAND_REMOVE_AUTOLOAD, + PA_COMMAND_GET_AUTOLOAD_INFO, + PA_COMMAND_GET_AUTOLOAD_INFO_LIST, + PA_COMMAND_GET_RECORD_LATENCY, + PA_COMMAND_CORK_RECORD_STREAM, + PA_COMMAND_FLUSH_RECORD_STREAM, + PA_COMMAND_PREBUF_PLAYBACK_STREAM, + PA_COMMAND_MAX +}; + +#define PA_NATIVE_COOKIE_LENGTH 256 +#define PA_NATIVE_COOKIE_FILE ".polypaudio-cookie" + +#define PA_NATIVE_DEFAULT_PORT 4713 + +#define PA_NATIVE_COOKIE_PROPERTY_NAME "protocol-native-cookie" +#define PA_NATIVE_SERVER_PROPERTY_NAME "protocol-native-server" + +#define PA_NATIVE_DEFAULT_UNIX_SOCKET "native" + + +PA_C_DECL_END + +#endif diff --git a/src/polypcore/oss-util.c b/src/polypcore/oss-util.c new file mode 100644 index 00000000..ae6772fd --- /dev/null +++ b/src/polypcore/oss-util.c @@ -0,0 +1,167 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "oss-util.h" +#include "util.h" +#include "log.h" + +int pa_oss_open(const char *device, int *mode, int* pcaps) { + int fd = -1; + assert(device && mode && (*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY)); + + if (*mode == O_RDWR) { + if ((fd = open(device, O_RDWR|O_NDELAY)) >= 0) { + int dcaps, *tcaps; + ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0); + + tcaps = pcaps ? pcaps : &dcaps; + + if (ioctl(fd, SNDCTL_DSP_GETCAPS, tcaps) < 0) { + pa_log(__FILE__": SNDCTL_DSP_GETCAPS: %s\n", strerror(errno)); + goto fail; + } + + if (*tcaps & DSP_CAP_DUPLEX) + return fd; + + goto fail; + } + + if ((fd = open(device, (*mode = O_WRONLY)|O_NDELAY)) < 0) { + if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY)) < 0) { + pa_log(__FILE__": open('%s'): %s\n", device, strerror(errno)); + goto fail; + } + } + } else { + if ((fd = open(device, *mode|O_NDELAY)) < 0) { + pa_log(__FILE__": open('%s'): %s\n", device, strerror(errno)); + goto fail; + } + } + + if (pcaps) { + if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) { + pa_log(__FILE__": SNDCTL_DSP_GETCAPS: %s\n", strerror(errno)); + goto fail; + } + } + + pa_fd_set_cloexec(fd, 1); + + return fd; + +fail: + if (fd >= 0) + close(fd); + return -1; +} + +int pa_oss_auto_format(int fd, pa_sample_spec *ss) { + int format, channels, speed, reqformat; + static const int format_trans[PA_SAMPLE_MAX] = { + [PA_SAMPLE_U8] = AFMT_U8, + [PA_SAMPLE_ALAW] = AFMT_A_LAW, + [PA_SAMPLE_ULAW] = AFMT_MU_LAW, + [PA_SAMPLE_S16LE] = AFMT_S16_LE, + [PA_SAMPLE_S16BE] = AFMT_S16_BE, + [PA_SAMPLE_FLOAT32LE] = AFMT_QUERY, /* not supported */ + [PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */ + }; + + assert(fd >= 0 && ss); + + reqformat = format = format_trans[ss->format]; + if (reqformat == AFMT_QUERY || ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != reqformat) { + format = AFMT_S16_NE; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) { + int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE; + format = f; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) { + format = AFMT_U8; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) { + pa_log(__FILE__": SNDCTL_DSP_SETFMT: %s\n", format != AFMT_U8 ? "No supported sample format" : strerror(errno)); + return -1; + } else + ss->format = PA_SAMPLE_U8; + } else + ss->format = f == AFMT_S16_LE ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE; + } else + ss->format = PA_SAMPLE_S16NE; + } + + channels = ss->channels; + if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) { + pa_log(__FILE__": SNDCTL_DSP_CHANNELS: %s\n", strerror(errno)); + return -1; + } + assert(channels); + ss->channels = channels; + + speed = ss->rate; + if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) { + pa_log(__FILE__": SNDCTL_DSP_SPEED: %s\n", strerror(errno)); + return -1; + } + assert(speed); + ss->rate = speed; + + return 0; +} + +static int simple_log2(int v) { + int k = 0; + + for (;;) { + v >>= 1; + if (!v) break; + k++; + } + + return k; +} + +int pa_oss_set_fragments(int fd, int nfrags, int frag_size) { + int arg; + arg = ((int) nfrags << 16) | simple_log2(frag_size); + + if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) { + pa_log(__FILE__": SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno)); + return -1; + } + + return 0; +} diff --git a/src/polypcore/oss-util.h b/src/polypcore/oss-util.h new file mode 100644 index 00000000..6b2746cc --- /dev/null +++ b/src/polypcore/oss-util.h @@ -0,0 +1,32 @@ +#ifndef fooossutilhfoo +#define fooossutilhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +int pa_oss_open(const char *device, int *mode, int* pcaps); +int pa_oss_auto_format(int fd, pa_sample_spec *ss); + +int pa_oss_set_fragments(int fd, int frags, int frag_size); + +#endif diff --git a/src/polypcore/packet.c b/src/polypcore/packet.c new file mode 100644 index 00000000..b3a2e074 --- /dev/null +++ b/src/polypcore/packet.c @@ -0,0 +1,69 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "packet.h" +#include "xmalloc.h" + +pa_packet* pa_packet_new(size_t length) { + pa_packet *p; + assert(length); + p = pa_xmalloc(sizeof(pa_packet)+length); + p->ref = 1; + p->length = length; + p->data = (uint8_t*) (p+1); + p->type = PA_PACKET_APPENDED; + return p; +} + +pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length) { + pa_packet *p; + assert(data && length); + p = pa_xmalloc(sizeof(pa_packet)); + p->ref = 1; + p->length = length; + p->data = data; + p->type = PA_PACKET_DYNAMIC; + return p; +} + +pa_packet* pa_packet_ref(pa_packet *p) { + assert(p && p->ref >= 1); + p->ref++; + return p; +} + +void pa_packet_unref(pa_packet *p) { + assert(p && p->ref >= 1); + p->ref--; + + if (p->ref == 0) { + if (p->type == PA_PACKET_DYNAMIC) + pa_xfree(p->data); + pa_xfree(p); + } +} diff --git a/src/polypcore/packet.h b/src/polypcore/packet.h new file mode 100644 index 00000000..0ac47485 --- /dev/null +++ b/src/polypcore/packet.h @@ -0,0 +1,41 @@ +#ifndef foopackethfoo +#define foopackethfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +typedef struct pa_packet { + enum { PA_PACKET_APPENDED, PA_PACKET_DYNAMIC } type; + unsigned ref; + size_t length; + uint8_t *data; +} pa_packet; + +pa_packet* pa_packet_new(size_t length); +pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length); + +pa_packet* pa_packet_ref(pa_packet *p); +void pa_packet_unref(pa_packet *p); + +#endif diff --git a/src/polypcore/parseaddr.c b/src/polypcore/parseaddr.c new file mode 100644 index 00000000..5e4c689c --- /dev/null +++ b/src/polypcore/parseaddr.c @@ -0,0 +1,112 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "xmalloc.h" +#include "util.h" +#include "parseaddr.h" + +/* Parse addresses in one of the following forms: + * HOSTNAME + * HOSTNAME:PORT + * [HOSTNAME] + * [HOSTNAME]:PORT + * + * Return a newly allocated string of the hostname and fill in *ret_port if specified */ + +static char *parse_host(const char *s, uint16_t *ret_port) { + assert(s && ret_port); + if (*s == '[') { + char *e; + if (!(e = strchr(s+1, ']'))) + return NULL; + + if (e[1] == ':') + *ret_port = atoi(e+2); + else if (e[1] != 0) + return NULL; + + return pa_xstrndup(s+1, e-s-1); + } else { + char *e; + + if (!(e = strrchr(s, ':'))) + return pa_xstrdup(s); + + *ret_port = atoi(e+1); + return pa_xstrndup(s, e-s); + } +} + +int pa_parse_address(const char *name, pa_parsed_address *ret_p) { + const char *p; + assert(name && ret_p); + memset(ret_p, 0, sizeof(pa_parsed_address)); + ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO; + + if (*name == '{') { + char hn[256], *pfx; + /* The URL starts with a host specification for detecting local connections */ + + if (!pa_get_host_name(hn, sizeof(hn))) + return -1; + + pfx = pa_sprintf_malloc("{%s}", hn); + if (!pa_startswith(name, pfx)) { + pa_xfree(pfx); + /* Not local */ + return -1; + } + + p = name + strlen(pfx); + pa_xfree(pfx); + } else + p = name; + + if (*p == '/') + ret_p->type = PA_PARSED_ADDRESS_UNIX; + else if (pa_startswith(p, "unix:")) { + ret_p->type = PA_PARSED_ADDRESS_UNIX; + p += sizeof("unix:")-1; + } else if (pa_startswith(p, "tcp:") || pa_startswith(p, "tcp4:")) { + ret_p->type = PA_PARSED_ADDRESS_TCP4; + p += sizeof("tcp:")-1; + } else if (pa_startswith(p, "tcp6:")) { + ret_p->type = PA_PARSED_ADDRESS_TCP6; + p += sizeof("tcp6:")-1; + } + + if (ret_p->type == PA_PARSED_ADDRESS_UNIX) + ret_p->path_or_host = pa_xstrdup(p); + else + if (!(ret_p->path_or_host = parse_host(p, &ret_p->port))) + return -1; + + + return 0; +} diff --git a/src/polypcore/parseaddr.h b/src/polypcore/parseaddr.h new file mode 100644 index 00000000..eff9dd3b --- /dev/null +++ b/src/polypcore/parseaddr.h @@ -0,0 +1,42 @@ +#ifndef fooparseaddrhfoo +#define fooparseaddrhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +typedef enum pa_parsed_address_type { + PA_PARSED_ADDRESS_UNIX, + PA_PARSED_ADDRESS_TCP4, + PA_PARSED_ADDRESS_TCP6, + PA_PARSED_ADDRESS_TCP_AUTO +} pa_parsed_address_type_t; + +typedef struct pa_parsed_address { + pa_parsed_address_type_t type; + char *path_or_host; + uint16_t port; +} pa_parsed_address; + +int pa_parse_address(const char *a, pa_parsed_address *ret_p); + +#endif diff --git a/src/polypcore/pdispatch.c b/src/polypcore/pdispatch.c new file mode 100644 index 00000000..5a50a0d5 --- /dev/null +++ b/src/polypcore/pdispatch.c @@ -0,0 +1,295 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "pdispatch.h" +#include "native-common.h" +#include "xmalloc.h" +#include "llist.h" +#include "log.h" +#include "util.h" + +/*#define DEBUG_OPCODES */ + +#ifdef DEBUG_OPCODES + +static const char *command_names[PA_COMMAND_MAX] = { + [PA_COMMAND_ERROR] = "ERROR", + [PA_COMMAND_TIMEOUT] = "TIMEOUT", + [PA_COMMAND_REPLY] = "REPLY", + [PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM", + [PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM", + [PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM", + [PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM", + [PA_COMMAND_AUTH] = "AUTH", + [PA_COMMAND_REQUEST] = "REQUEST", + [PA_COMMAND_EXIT] = "EXIT", + [PA_COMMAND_SET_CLIENT_NAME] = "SET_CLIENT_NAME", + [PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK", + [PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE", + [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM", + [PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED", + [PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED", + [PA_COMMAND_STAT] = "STAT", + [PA_COMMAND_GET_PLAYBACK_LATENCY] = "PLAYBACK_LATENCY", + [PA_COMMAND_CREATE_UPLOAD_STREAM] = "CREATE_UPLOAD_STREAM", + [PA_COMMAND_DELETE_UPLOAD_STREAM] = "DELETE_UPLOAD_STREAM", + [PA_COMMAND_FINISH_UPLOAD_STREAM] = "FINISH_UPLOAD_STREAM", + [PA_COMMAND_PLAY_SAMPLE] = "PLAY_SAMPLE", + [PA_COMMAND_REMOVE_SAMPLE] = "REMOVE_SAMPLE", + [PA_COMMAND_GET_SERVER_INFO] = "GET_SERVER_INFO", + [PA_COMMAND_GET_SINK_INFO] = "GET_SINK_INFO", + [PA_COMMAND_GET_SINK_INFO_LIST] = "GET_SINK_INFO_LIST", + [PA_COMMAND_GET_SOURCE_INFO] = "GET_SOURCE_INFO", + [PA_COMMAND_GET_SOURCE_INFO_LIST] = "GET_SOURCE_INFO_LIST", + [PA_COMMAND_GET_MODULE_INFO] = "GET_MODULE_INFO", + [PA_COMMAND_GET_MODULE_INFO_LIST] = "GET_MODULE_INFO_LIST", + [PA_COMMAND_GET_CLIENT_INFO] = "GET_CLIENT_INFO", + [PA_COMMAND_GET_CLIENT_INFO_LIST] = "GET_CLIENT_INFO_LIST", + [PA_COMMAND_GET_SAMPLE_INFO] = "GET_SAMPLE_INFO", + [PA_COMMAND_GET_SAMPLE_INFO_LIST] = "GET_SAMPLE_INFO_LIST", + [PA_COMMAND_GET_SINK_INPUT_INFO] = "GET_SINK_INPUT_INFO", + [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = "GET_SINK_INPUT_INFO_LIST", + [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = "GET_SOURCE_OUTPUT_INFO", + [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = "GET_SOURCE_OUTPUT_INFO_LIST", + [PA_COMMAND_SUBSCRIBE] = "SUBSCRIBE", + [PA_COMMAND_SUBSCRIBE_EVENT] = "SUBSCRIBE_EVENT", + [PA_COMMAND_SET_SINK_VOLUME] = "SET_SINK_VOLUME", + [PA_COMMAND_SET_SINK_INPUT_VOLUME] = "SET_SINK_INPUT_VOLUME", + [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = "TRIGGER_PLAYBACK_STREAM", + [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = "FLUSH_PLAYBACK_STREAM", + [PA_COMMAND_CORK_PLAYBACK_STREAM] = "CORK_PLAYBACK_STREAM", + [PA_COMMAND_GET_AUTOLOAD_INFO] = "GET_AUTOLOAD_INFO", + [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = "GET_AUTOLOAD_INFO_LIST", +}; + +#endif + +struct reply_info { + pa_pdispatch *pdispatch; + PA_LLIST_FIELDS(struct reply_info); + pa_pdispatch_callback callback; + void *userdata; + uint32_t tag; + pa_time_event *time_event; +}; + +struct pa_pdispatch { + int ref; + pa_mainloop_api *mainloop; + const pa_pdispatch_callback *callback_table; + unsigned n_commands; + PA_LLIST_HEAD(struct reply_info, replies); + pa_pdispatch_drain_callback drain_callback; + void *drain_userdata; +}; + +static void reply_info_free(struct reply_info *r) { + assert(r && r->pdispatch && r->pdispatch->mainloop); + + if (r->time_event) + r->pdispatch->mainloop->time_free(r->time_event); + + PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r); + + pa_xfree(r); +} + +pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_callback*table, unsigned entries) { + pa_pdispatch *pd; + assert(mainloop); + + assert((entries && table) || (!entries && !table)); + + pd = pa_xmalloc(sizeof(pa_pdispatch)); + pd->ref = 1; + pd->mainloop = mainloop; + pd->callback_table = table; + pd->n_commands = entries; + PA_LLIST_HEAD_INIT(pa_reply_info, pd->replies); + pd->drain_callback = NULL; + pd->drain_userdata = NULL; + + return pd; +} + +static void pdispatch_free(pa_pdispatch *pd) { + assert(pd); + + while (pd->replies) + reply_info_free(pd->replies); + + pa_xfree(pd); +} + +static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) { + pa_pdispatch_callback callback; + void *userdata; + uint32_t tag; + assert(r); + + pa_pdispatch_ref(pd); + + callback = r->callback; + userdata = r->userdata; + tag = r->tag; + + reply_info_free(r); + + callback(pd, command, tag, ts, userdata); + + if (pd->drain_callback && !pa_pdispatch_is_pending(pd)) + pd->drain_callback(pd, pd->drain_userdata); + + pa_pdispatch_unref(pd); +} + +int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, void *userdata) { + uint32_t tag, command; + pa_tagstruct *ts = NULL; + int ret = -1; + assert(pd && packet && packet->data); + + pa_pdispatch_ref(pd); + + if (packet->length <= 8) + goto finish; + + ts = pa_tagstruct_new(packet->data, packet->length); + assert(ts); + + if (pa_tagstruct_getu32(ts, &command) < 0 || + pa_tagstruct_getu32(ts, &tag) < 0) + goto finish; + +#ifdef DEBUG_OPCODES +{ + char t[256]; + char const *p; + if (!(p = command_names[command])) + snprintf((char*) (p = t), sizeof(t), "%u", command); + + pa_log(__FILE__": Recieved opcode <%s>\n", p); +} +#endif + + if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) { + struct reply_info *r; + + for (r = pd->replies; r; r = r->next) + if (r->tag == tag) + break; + + if (r) + run_action(pd, r, command, ts); + + } else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) { + const pa_pdispatch_callback *c = pd->callback_table+command; + + (*c)(pd, command, tag, ts, userdata); + } else { + pa_log(__FILE__": Recieved unsupported command %u\n", command); + goto finish; + } + + ret = 0; + +finish: + if (ts) + pa_tagstruct_free(ts); + + pa_pdispatch_unref(pd); + + return ret; +} + +static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { + struct reply_info*r = userdata; + assert(r && r->time_event == e && r->pdispatch && r->pdispatch->mainloop == m && r->callback); + + run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL); +} + +void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_callback cb, void *userdata) { + struct reply_info *r; + struct timeval tv; + assert(pd && pd->ref >= 1 && cb); + + r = pa_xmalloc(sizeof(struct reply_info)); + r->pdispatch = pd; + r->callback = cb; + r->userdata = userdata; + r->tag = tag; + + pa_gettimeofday(&tv); + tv.tv_sec += timeout; + + r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r); + assert(r->time_event); + + PA_LLIST_PREPEND(struct reply_info, pd->replies, r); +} + +int pa_pdispatch_is_pending(pa_pdispatch *pd) { + assert(pd); + + return !!pd->replies; +} + +void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) { + assert(pd); + assert(!cb || pa_pdispatch_is_pending(pd)); + + pd->drain_callback = cb; + pd->drain_userdata = userdata; +} + +void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) { + struct reply_info *r, *n; + assert(pd); + + for (r = pd->replies; r; r = n) { + n = r->next; + + if (r->userdata == userdata) + reply_info_free(r); + } +} + +void pa_pdispatch_unref(pa_pdispatch *pd) { + assert(pd && pd->ref >= 1); + + if (!(--(pd->ref))) + pdispatch_free(pd); +} + +pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) { + assert(pd && pd->ref >= 1); + pd->ref++; + return pd; +} diff --git a/src/polypcore/pdispatch.h b/src/polypcore/pdispatch.h new file mode 100644 index 00000000..fdd4d7f5 --- /dev/null +++ b/src/polypcore/pdispatch.h @@ -0,0 +1,50 @@ +#ifndef foopdispatchhfoo +#define foopdispatchhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include "tagstruct.h" +#include "packet.h" +#include + +typedef struct pa_pdispatch pa_pdispatch; + +typedef void (*pa_pdispatch_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); + +pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, const pa_pdispatch_callback*table, unsigned entries); +void pa_pdispatch_unref(pa_pdispatch *pd); +pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd); + +int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*p, void *userdata); + +void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_callback callback, void *userdata); + +int pa_pdispatch_is_pending(pa_pdispatch *pd); + +typedef void (*pa_pdispatch_drain_callback)(pa_pdispatch *pd, void *userdata); +void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_callback callback, void *userdata); + +/* Remove all reply slots with the give userdata parameter */ +void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata); + +#endif diff --git a/src/polypcore/pid.c b/src/polypcore/pid.c new file mode 100644 index 00000000..ae3dc7f5 --- /dev/null +++ b/src/polypcore/pid.c @@ -0,0 +1,286 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_WINDOWS_H +#include +#endif + +#include "pid.h" +#include "util.h" +#include "log.h" + +/* Read the PID data from the file descriptor fd, and return it. If no + * pid could be read, return 0, on failure (pid_t) -1 */ +static pid_t read_pid(const char *fn, int fd) { + ssize_t r; + char t[20], *e; + uint32_t pid; + + assert(fn && fd >= 0); + + if ((r = pa_loop_read(fd, t, sizeof(t)-1)) < 0) { + pa_log(__FILE__": WARNING: failed to read PID file '%s': %s\n", fn, strerror(errno)); + return (pid_t) -1; + } + + if (r == 0) + return (pid_t) 0; + + t[r] = 0; + if ((e = strchr(t, '\n'))) + *e = 0; + + if (pa_atou(t, &pid) < 0) { + pa_log(__FILE__": WARNING: failed to parse PID file '%s'\n", fn); + return (pid_t) -1; + } + + return (pid_t) pid; +} + +static int open_pid_file(const char *fn, int mode) { + int fd = -1; + int lock = -1; + + for (;;) { + struct stat st; + + pa_make_secure_parent_dir(fn); + + if ((fd = open(fn, mode, S_IRUSR|S_IWUSR)) < 0) { + if (mode != O_RDONLY || errno != ENOENT) + pa_log(__FILE__": WARNING: failed to open PID file '%s': %s\n", fn, strerror(errno)); + goto fail; + } + + /* Try to lock the file. If that fails, go without */ + if (pa_lock_fd(fd, 1) < 0) + goto fail; + + if (fstat(fd, &st) < 0) { + pa_log(__FILE__": Failed to fstat() PID file '%s': %s\n", fn, strerror(errno)); + goto fail; + } + + /* Does the file still exist in the file system? When ye, w're done, otherwise restart */ + if (st.st_nlink >= 1) + break; + + if (pa_lock_fd(fd, 0) < 0) + goto fail; + + if (close(fd) < 0) { + pa_log(__FILE__": Failed to close file '%s': %s\n", fn, strerror(errno)); + goto fail; + } + + fd = -1; + } + + return fd; + +fail: + + if (fd < 0) { + if (lock >= 0) + pa_lock_fd(fd, 0); + + close(fd); + } + + return -1; +} + +/* Create a new PID file for the current process. */ +int pa_pid_file_create(void) { + int fd = -1; + int ret = -1; + char fn[PATH_MAX]; + char t[20]; + pid_t pid; + size_t l; + +#ifdef OS_IS_WIN32 + HANDLE process; +#endif + + pa_runtime_path("pid", fn, sizeof(fn)); + + if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0) + goto fail; + + if ((pid = read_pid(fn, fd)) == (pid_t) -1) + pa_log(__FILE__": corrupt PID file, overwriting.\n"); + else if (pid > 0) { +#ifdef OS_IS_WIN32 + if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) { + CloseHandle(process); +#else + if (kill(pid, 0) >= 0 || errno != ESRCH) { +#endif + pa_log(__FILE__": daemon already running.\n"); + goto fail; + } + + pa_log(__FILE__": stale PID file, overwriting.\n"); + } + + /* Overwrite the current PID file */ + if (lseek(fd, 0, SEEK_SET) == (off_t) -1 || ftruncate(fd, 0) < 0) { + pa_log(__FILE__": failed to truncate PID fil: %s.\n", strerror(errno)); + goto fail; + } + + snprintf(t, sizeof(t), "%lu\n", (unsigned long) getpid()); + l = strlen(t); + + if (pa_loop_write(fd, t, l) != (ssize_t) l) { + pa_log(__FILE__": failed to write PID file.\n"); + goto fail; + } + + ret = 0; + +fail: + if (fd >= 0) { + pa_lock_fd(fd, 0); + close(fd); + } + + return ret; +} + +/* Remove the PID file, if it is ours */ +int pa_pid_file_remove(void) { + int fd = -1; + char fn[PATH_MAX]; + int ret = -1; + pid_t pid; + + pa_runtime_path("pid", fn, sizeof(fn)); + + if ((fd = open_pid_file(fn, O_RDWR)) < 0) { + pa_log(__FILE__": WARNING: failed to open PID file '%s': %s\n", fn, strerror(errno)); + goto fail; + } + + if ((pid = read_pid(fn, fd)) == (pid_t) -1) + goto fail; + + if (pid != getpid()) { + pa_log(__FILE__": WARNING: PID file '%s' not mine!\n", fn); + goto fail; + } + + if (ftruncate(fd, 0) < 0) { + pa_log(__FILE__": failed to truncate PID file '%s': %s\n", fn, strerror(errno)); + goto fail; + } + +#ifdef OS_IS_WIN32 + pa_lock_fd(fd, 0); + close(fd); + fd = -1; +#endif + + if (unlink(fn) < 0) { + pa_log(__FILE__": failed to remove PID file '%s': %s\n", fn, strerror(errno)); + goto fail; + } + + ret = 0; + +fail: + + if (fd >= 0) { + pa_lock_fd(fd, 0); + close(fd); + } + + return ret; +} + +/* Check whether the daemon is currently running, i.e. if a PID file + * exists and the PID therein too. Returns 0 on succcess, -1 + * otherwise. If pid is non-NULL and a running daemon was found, + * return its PID therein */ +int pa_pid_file_check_running(pid_t *pid) { + return pa_pid_file_kill(0, pid); +} + +#ifndef OS_IS_WIN32 + +/* Kill a current running daemon. Return non-zero on success, -1 + * otherwise. If successful *pid contains the PID of the daemon + * process. */ +int pa_pid_file_kill(int sig, pid_t *pid) { + int fd = -1; + char fn[PATH_MAX]; + int ret = -1; + pid_t _pid; + + if (!pid) + pid = &_pid; + + pa_runtime_path("pid", fn, sizeof(fn)); + + if ((fd = open_pid_file(fn, O_RDONLY)) < 0) + goto fail; + + if ((*pid = read_pid(fn, fd)) == (pid_t) -1) + goto fail; + + ret = kill(*pid, sig); + +fail: + + if (fd >= 0) { + pa_lock_fd(fd, 0); + close(fd); + } + + return ret; + +} + +#else /* OS_IS_WIN32 */ + +int pa_pid_file_kill(int sig, pid_t *pid) { + return -1; +} + +#endif diff --git a/src/polypcore/pid.h b/src/polypcore/pid.h new file mode 100644 index 00000000..906ab6da --- /dev/null +++ b/src/polypcore/pid.h @@ -0,0 +1,30 @@ +#ifndef foopidhfoo +#define foopidhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +int pa_pid_file_create(void); +int pa_pid_file_remove(void); +int pa_pid_file_check_running(pid_t *pid); +int pa_pid_file_kill(int sig, pid_t *pid); + +#endif diff --git a/src/polypcore/play-memchunk.c b/src/polypcore/play-memchunk.c new file mode 100644 index 00000000..e24d4427 --- /dev/null +++ b/src/polypcore/play-memchunk.c @@ -0,0 +1,118 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "play-memchunk.h" +#include "sink-input.h" +#include "xmalloc.h" +#include "gccmacro.h" + +static void sink_input_kill(pa_sink_input *i) { + pa_memchunk *c; + assert(i && i->userdata); + c = i->userdata; + + pa_sink_input_disconnect(i); + pa_sink_input_unref(i); + + pa_memblock_unref(c->memblock); + pa_xfree(c); +} + +static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) { + pa_memchunk *c; + assert(i && chunk && i->userdata); + c = i->userdata; + + if (c->length <= 0) + return -1; + + assert(c->memblock && c->memblock->length); + *chunk = *c; + pa_memblock_ref(c->memblock); + + return 0; +} + +static void si_kill(PA_GCC_UNUSED pa_mainloop_api *m, void *i) { + sink_input_kill(i); +} + +static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) { + pa_memchunk *c; + assert(i && length && i->userdata); + c = i->userdata; + + assert(!memcmp(chunk, c, sizeof(chunk))); + assert(length <= c->length); + + c->length -= length; + c->index += length; + + if (c->length <= 0) + pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i); +} + +int pa_play_memchunk( + pa_sink *sink, + const char *name, + const pa_sample_spec *ss, + const pa_channel_map *map, + const pa_memchunk *chunk, + pa_cvolume *cvolume) { + + pa_sink_input *si; + pa_memchunk *nchunk; + + assert(sink); + assert(ss); + assert(chunk); + + if (cvolume && pa_cvolume_is_muted(cvolume)) + return 0; + + if (!(si = pa_sink_input_new(sink, name, __FILE__, ss, map, 0, PA_RESAMPLER_INVALID))) + return -1; + + if (cvolume) + si->volume = *cvolume; + + si->peek = sink_input_peek; + si->drop = sink_input_drop; + si->kill = sink_input_kill; + + si->userdata = nchunk = pa_xnew(pa_memchunk, 1); + *nchunk = *chunk; + + pa_memblock_ref(chunk->memblock); + + pa_sink_notify(sink); + + return 0; +} diff --git a/src/polypcore/play-memchunk.h b/src/polypcore/play-memchunk.h new file mode 100644 index 00000000..fc0654a6 --- /dev/null +++ b/src/polypcore/play-memchunk.h @@ -0,0 +1,36 @@ +#ifndef fooplaychunkhfoo +#define fooplaychunkhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "sink.h" +#include "memchunk.h" + +int pa_play_memchunk( + pa_sink *sink, + const char *name, + const pa_sample_spec *ss, + const pa_channel_map *map, + const pa_memchunk *chunk, + pa_cvolume *cvolume); + +#endif diff --git a/src/polypcore/poll.c b/src/polypcore/poll.c new file mode 100644 index 00000000..193fc812 --- /dev/null +++ b/src/polypcore/poll.c @@ -0,0 +1,190 @@ +/* $Id$ */ + +/*** + Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc. + Copyright (C) 2005, Cendio AB. + This file is part of polypaudio. + Based on work for the GNU C Library. + + polypaudio is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with polypaudio; If not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +***/ + +/* Poll the file descriptors described by the NFDS structures starting at + FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for + an event to occur; if TIMEOUT is -1, block until an event occurs. + Returns the number of file descriptors with events, zero if timed out, + or -1 for errors. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#include "winsock.h" + +#ifndef HAVE_SYS_POLL_H + +#include "util.h" +#include "poll.h" + +int poll (struct pollfd *fds, unsigned long int nfds, int timeout) { + struct timeval tv; + fd_set rset, wset, xset; + struct pollfd *f; + int ready; + int maxfd = 0; + char data[64]; + + FD_ZERO (&rset); + FD_ZERO (&wset); + FD_ZERO (&xset); + + if (nfds == 0) { + if (timeout >= 0) { + pa_msleep(timeout); + return 0; + } + +#ifdef OS_IS_WIN32 + /* + * Windows does not support signals properly so waiting for them would + * mean a deadlock. + */ + pa_msleep(100); + return 0; +#else + return select(0, NULL, NULL, NULL, NULL); +#endif + } + + for (f = fds; f < &fds[nfds]; ++f) { + if (f->fd != -1) { + if (f->events & POLLIN) + FD_SET (f->fd, &rset); + if (f->events & POLLOUT) + FD_SET (f->fd, &wset); + if (f->events & POLLPRI) + FD_SET (f->fd, &xset); + if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI))) + maxfd = f->fd; + } + } + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset, + SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset, + SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv)); + if ((ready == -1) && (errno == EBADF)) { + ready = 0; + + FD_ZERO (&rset); + FD_ZERO (&wset); + FD_ZERO (&xset); + + maxfd = -1; + + for (f = fds; f < &fds[nfds]; ++f) { + if (f->fd != -1) { + fd_set sngl_rset, sngl_wset, sngl_xset; + + FD_ZERO (&sngl_rset); + FD_ZERO (&sngl_wset); + FD_ZERO (&sngl_xset); + + if (f->events & POLLIN) + FD_SET (f->fd, &sngl_rset); + if (f->events & POLLOUT) + FD_SET (f->fd, &sngl_wset); + if (f->events & POLLPRI) + FD_SET (f->fd, &sngl_xset); + if (f->events & (POLLIN|POLLOUT|POLLPRI)) { + struct timeval singl_tv; + + singl_tv.tv_sec = 0; + singl_tv.tv_usec = 0; + + if (select((SELECT_TYPE_ARG1) f->fd, SELECT_TYPE_ARG234 &rset, + SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset, + SELECT_TYPE_ARG5 &singl_tv) != -1) { + if (f->events & POLLIN) + FD_SET (f->fd, &rset); + if (f->events & POLLOUT) + FD_SET (f->fd, &wset); + if (f->events & POLLPRI) + FD_SET (f->fd, &xset); + if (f->fd > maxfd && (f->events & (POLLIN|POLLOUT|POLLPRI))) + maxfd = f->fd; + ++ready; + } else if (errno == EBADF) + f->revents |= POLLNVAL; + } + } + } + + if (ready) { + /* Linux alters the tv struct... but it shouldn't matter here ... + * as we're going to be a little bit out anyway as we've just eaten + * more than a couple of cpu cycles above */ + ready = select ((SELECT_TYPE_ARG1) maxfd + 1, SELECT_TYPE_ARG234 &rset, + SELECT_TYPE_ARG234 &wset, SELECT_TYPE_ARG234 &xset, + SELECT_TYPE_ARG5 (timeout == -1 ? NULL : &tv)); + } + } + +#ifdef OS_IS_WIN32 + errno = WSAGetLastError(); +#endif + + if (ready > 0) { + ready = 0; + for (f = fds; f < &fds[nfds]; ++f) { + f->revents = 0; + if (f->fd != -1) { + if (FD_ISSET (f->fd, &rset)) { + /* support for POLLHUP. An hung up descriptor does not + increase the return value! */ + if (recv (f->fd, data, 64, MSG_PEEK) == -1) { + if (errno == ESHUTDOWN || errno == ECONNRESET || + errno == ECONNABORTED || errno == ENETRESET) { + fprintf(stderr, "Hangup\n"); + f->revents |= POLLHUP; + } + } + + if (f->revents == 0) + f->revents |= POLLIN; + } + if (FD_ISSET (f->fd, &wset)) + f->revents |= POLLOUT; + if (FD_ISSET (f->fd, &xset)) + f->revents |= POLLPRI; + } + if (f->revents) + ready++; + } + } + + return ready; +} + +#endif /* HAVE_SYS_POLL_H */ diff --git a/src/polypcore/poll.h b/src/polypcore/poll.h new file mode 100644 index 00000000..c201014e --- /dev/null +++ b/src/polypcore/poll.h @@ -0,0 +1,57 @@ +/* $Id$ */ + +/*** + Compatibility definitions for System V `poll' interface. + Copyright (C) 1994,96,97,98,99,2000,2001,2004 Free Software Foundation, Inc. + Copyright (C) 2005, Cendio AB. + This file is part of polypaudio. + Based on work for the GNU C Library. + + polypaudio is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with polypaudio; If not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +***/ + +/* Event types that can be polled for. These bits may be set in `events' + to indicate the interesting event types; they will appear in `revents' + to indicate the status of the file descriptor. */ +#define POLLIN 0x001 /* There is data to read. */ +#define POLLPRI 0x002 /* There is urgent data to read. */ +#define POLLOUT 0x004 /* Writing now will not block. */ + +/* Event types always implicitly polled for. These bits need not be set in + `events', but they will appear in `revents' to indicate the status of + the file descriptor. */ +#define POLLERR 0x008 /* Error condition. */ +#define POLLHUP 0x010 /* Hung up. */ +#define POLLNVAL 0x020 /* Invalid polling request. */ + + +/* Type used for the number of file descriptors. */ +typedef unsigned long int nfds_t; + +/* Data structure describing a polling request. */ +struct pollfd + { + int fd; /* File descriptor to poll. */ + short int events; /* Types of events poller cares about. */ + short int revents; /* Types of events that actually occurred. */ + }; + +/* Poll the file descriptors described by the NFDS structures starting at + FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for + an event to occur; if TIMEOUT is -1, block until an event occurs. + Returns the number of file descriptors with events, zero if timed out, + or -1 for errors. */ +extern int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout); diff --git a/src/polypcore/props.c b/src/polypcore/props.c new file mode 100644 index 00000000..df748c1e --- /dev/null +++ b/src/polypcore/props.c @@ -0,0 +1,119 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "xmalloc.h" +#include "props.h" +#include "log.h" + +typedef struct pa_property { + char *name; /* Points to memory allocated by the property subsystem */ + void *data; /* Points to memory maintained by the caller */ +} pa_property; + +/* Allocate a new property object */ +static pa_property* property_new(const char *name, void *data) { + pa_property* p; + assert(name && data); + + p = pa_xmalloc(sizeof(pa_property)); + p->name = pa_xstrdup(name); + p->data = data; + + return p; +} + +/* Free a property object */ +static void property_free(pa_property *p) { + assert(p); + + pa_xfree(p->name); + pa_xfree(p); +} + +void* pa_property_get(pa_core *c, const char *name) { + pa_property *p; + assert(c && name && c->properties); + + if (!(p = pa_hashmap_get(c->properties, name))) + return NULL; + + return p->data; +} + +int pa_property_set(pa_core *c, const char *name, void *data) { + pa_property *p; + assert(c && name && data && c->properties); + + if (pa_hashmap_get(c->properties, name)) + return -1; + + p = property_new(name, data); + pa_hashmap_put(c->properties, p->name, p); + return 0; +} + +int pa_property_remove(pa_core *c, const char *name) { + pa_property *p; + assert(c && name && c->properties); + + if (!(p = pa_hashmap_remove(c->properties, name))) + return -1; + + property_free(p); + return 0; +} + +void pa_property_init(pa_core *c) { + assert(c); + + c->properties = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); +} + +void pa_property_cleanup(pa_core *c) { + assert(c); + + if (!c->properties) + return; + + assert(!pa_hashmap_size(c->properties)); + + pa_hashmap_free(c->properties, NULL, NULL); + c->properties = NULL; + +} + +void pa_property_dump(pa_core *c, pa_strbuf *s) { + void *state = NULL; + pa_property *p; + assert(c && s); + + while ((p = pa_hashmap_iterate(c->properties, &state, NULL))) + pa_strbuf_printf(s, "[%s] -> [%p]\n", p->name, p->data); +} + +int pa_property_replace(pa_core *c, const char *name, void *data) { + assert(c && name); + + pa_property_remove(c, name); + return pa_property_set(c, name, data); +} diff --git a/src/polypcore/props.h b/src/polypcore/props.h new file mode 100644 index 00000000..5abf8787 --- /dev/null +++ b/src/polypcore/props.h @@ -0,0 +1,58 @@ +#ifndef foopropshfoo +#define foopropshfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "core.h" +#include "strbuf.h" + +/* The property subsystem is to be used to share data between + * modules. Consider them to be kind of "global" variables for a + * core. Why not use the hashmap functions directly? The hashmap + * functions copy neither the key nor value, while this property + * system copies the key. Users of this system have to think about + * reference counting themselves. */ + +/* Return a pointer to the value of the specified property. */ +void* pa_property_get(pa_core *c, const char *name); + +/* Set the property 'name' to 'data'. This function fails in case a + * property by this name already exists. The property data is not + * copied or reference counted. This is the caller's job. */ +int pa_property_set(pa_core *c, const char *name, void *data); + +/* Remove the specified property. Return non-zero on failure */ +int pa_property_remove(pa_core *c, const char *name); + +/* A combination of pa_property_remove() and pa_property_set() */ +int pa_property_replace(pa_core *c, const char *name, void *data); + +/* Free all memory used by the property system */ +void pa_property_cleanup(pa_core *c); + +/* Initialize the properties subsystem */ +void pa_property_init(pa_core *c); + +/* Dump the current set of properties */ +void pa_property_dump(pa_core *c, pa_strbuf *s); + +#endif diff --git a/src/polypcore/protocol-cli.c b/src/polypcore/protocol-cli.c new file mode 100644 index 00000000..d2d73550 --- /dev/null +++ b/src/polypcore/protocol-cli.c @@ -0,0 +1,95 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "protocol-cli.h" +#include "cli.h" +#include "xmalloc.h" +#include "log.h" + +/* Don't allow more than this many concurrent connections */ +#define MAX_CONNECTIONS 25 + +struct pa_protocol_cli { + pa_module *module; + pa_core *core; + pa_socket_server*server; + pa_idxset *connections; +}; + +static void cli_eof_cb(pa_cli*c, void*userdata) { + pa_protocol_cli *p = userdata; + assert(p); + pa_idxset_remove_by_data(p->connections, c, NULL); + pa_cli_free(c); +} + +static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) { + pa_protocol_cli *p = userdata; + pa_cli *c; + assert(s && io && p); + + if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) { + pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS); + pa_iochannel_free(io); + return; + } + + c = pa_cli_new(p->core, io, p->module); + assert(c); + pa_cli_set_eof_callback(c, cli_eof_cb, p); + + pa_idxset_put(p->connections, c, NULL); +} + +pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) { + pa_protocol_cli* p; + assert(core && server); + + p = pa_xmalloc(sizeof(pa_protocol_cli)); + p->module = m; + p->core = core; + p->server = server; + p->connections = pa_idxset_new(NULL, NULL); + + pa_socket_server_set_callback(p->server, on_connection, p); + + return p; +} + +static void free_connection(void *p, PA_GCC_UNUSED void *userdata) { + assert(p); + pa_cli_free(p); +} + +void pa_protocol_cli_free(pa_protocol_cli *p) { + assert(p); + + pa_idxset_free(p->connections, free_connection, NULL); + pa_socket_server_unref(p->server); + pa_xfree(p); +} diff --git a/src/polypcore/protocol-cli.h b/src/polypcore/protocol-cli.h new file mode 100644 index 00000000..aed733c1 --- /dev/null +++ b/src/polypcore/protocol-cli.h @@ -0,0 +1,35 @@ +#ifndef fooprotocolclihfoo +#define fooprotocolclihfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "core.h" +#include "socket-server.h" +#include "module.h" +#include "modargs.h" + +typedef struct pa_protocol_cli pa_protocol_cli; + +pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma); +void pa_protocol_cli_free(pa_protocol_cli *n); + +#endif diff --git a/src/polypcore/protocol-esound.c b/src/polypcore/protocol-esound.c new file mode 100644 index 00000000..14f237c7 --- /dev/null +++ b/src/polypcore/protocol-esound.c @@ -0,0 +1,1166 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#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 +#include "scache.h" +#include "sample-util.h" +#include "authkey.h" +#include "namereg.h" +#include "xmalloc.h" +#include "log.h" +#include "util.h" +#include "endianmacros.h" + +/* Don't accept more connection than this */ +#define MAX_CONNECTIONS 10 + +/* Kick a client if it doesn't authenticate within this time */ +#define AUTH_TIMEOUT 5 + +#define DEFAULT_COOKIE_FILE ".esd_auth" + +#define PLAYBACK_BUFFER_SECONDS (.5) +#define PLAYBACK_BUFFER_FRAGMENTS (10) +#define RECORD_BUFFER_SECONDS (5) +#define RECORD_BUFFER_FRAGMENTS (100) + +#define MAX_CACHE_SAMPLE_SIZE (1024000) + +#define SCACHE_PREFIX "esound." + +/* This is heavily based on esound's code */ + +struct connection { + uint32_t index; + int dead; + pa_protocol_esound *protocol; + pa_iochannel *io; + pa_client *client; + int authorized, swap_byte_order; + void *write_data; + size_t write_data_alloc, write_data_index, write_data_length; + void *read_data; + size_t read_data_alloc, read_data_length; + esd_proto_t request; + esd_client_state_t state; + pa_sink_input *sink_input; + pa_source_output *source_output; + pa_memblockq *input_memblockq, *output_memblockq; + pa_defer_event *defer_event; + + struct { + pa_memblock *current_memblock; + size_t memblock_index, fragment_size; + } playback; + + struct { + pa_memchunk memchunk; + char *name; + pa_sample_spec sample_spec; + } scache; + + pa_time_event *auth_timeout_event; +}; + +struct pa_protocol_esound { + int public; + pa_module *module; + pa_core *core; + pa_socket_server *server; + pa_idxset *connections; + char *sink_name, *source_name; + unsigned n_player; + uint8_t esd_key[ESD_KEY_LEN]; +}; + +typedef struct proto_handler { + size_t data_length; + int (*proc)(struct connection *c, esd_proto_t request, const void *data, size_t length); + const char *description; +} esd_proto_handler_info_t; + +static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length); +static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk); +static void sink_input_kill_cb(pa_sink_input *i); +static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i); +static pa_usec_t source_output_get_latency_cb(pa_source_output *o); + +static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk); +static void source_output_kill_cb(pa_source_output *o); + +static int esd_proto_connect(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_stream_play(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_get_latency(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_server_info(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_sample_cache(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_sample_get_id(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_standby_or_resume(struct connection *c, esd_proto_t request, const void *data, size_t length); + +/* the big map of protocol handler info */ +static struct proto_handler proto_map[ESD_PROTO_MAX] = { + { ESD_KEY_LEN + sizeof(int), esd_proto_connect, "connect" }, + { ESD_KEY_LEN + sizeof(int), NULL, "lock" }, + { ESD_KEY_LEN + sizeof(int), NULL, "unlock" }, + + { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_play, "stream play" }, + { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream rec" }, + { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream mon" }, + + { ESD_NAME_MAX + 3 * sizeof(int), esd_proto_sample_cache, "sample cache" }, /* 6 */ + { sizeof(int), esd_proto_sample_free_or_play, "sample free" }, + { sizeof(int), esd_proto_sample_free_or_play, "sample play" }, /* 8 */ + { sizeof(int), NULL, "sample loop" }, + { sizeof(int), NULL, "sample stop" }, + { -1, NULL, "TODO: sample kill" }, + + { ESD_KEY_LEN + sizeof(int), esd_proto_standby_or_resume, "standby" }, /* NOOP! */ + { ESD_KEY_LEN + sizeof(int), esd_proto_standby_or_resume, "resume" }, /* NOOP! */ /* 13 */ + + { ESD_NAME_MAX, esd_proto_sample_get_id, "sample getid" }, /* 14 */ + { ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" }, + + { sizeof(int), esd_proto_server_info, "server info" }, + { sizeof(int), esd_proto_all_info, "all info" }, + { -1, NULL, "TODO: subscribe" }, + { -1, NULL, "TODO: unsubscribe" }, + + { 3 * sizeof(int), esd_proto_stream_pan, "stream pan"}, + { 3 * sizeof(int), NULL, "sample pan" }, + + { sizeof(int), NULL, "standby mode" }, + { 0, esd_proto_get_latency, "get latency" } +}; + + +static void connection_free(struct connection *c) { + assert(c); + pa_idxset_remove_by_data(c->protocol->connections, c, NULL); + + if (c->state == ESD_STREAMING_DATA) + c->protocol->n_player--; + + pa_client_free(c->client); + + if (c->sink_input) { + pa_sink_input_disconnect(c->sink_input); + pa_sink_input_unref(c->sink_input); + } + + if (c->source_output) { + pa_source_output_disconnect(c->source_output); + pa_source_output_unref(c->source_output); + } + + if (c->input_memblockq) + pa_memblockq_free(c->input_memblockq); + if (c->output_memblockq) + pa_memblockq_free(c->output_memblockq); + + if (c->playback.current_memblock) + pa_memblock_unref(c->playback.current_memblock); + + pa_xfree(c->read_data); + pa_xfree(c->write_data); + + if (c->io) + pa_iochannel_free(c->io); + + if (c->defer_event) + c->protocol->core->mainloop->defer_free(c->defer_event); + + if (c->scache.memchunk.memblock) + pa_memblock_unref(c->scache.memchunk.memblock); + pa_xfree(c->scache.name); + + if (c->auth_timeout_event) + c->protocol->core->mainloop->time_free(c->auth_timeout_event); + + pa_xfree(c); +} + +static void* connection_write(struct connection *c, size_t length) { + size_t t, i; + assert(c); + + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); + c->protocol->core->mainloop->defer_enable(c->defer_event, 1); + + t = c->write_data_length+length; + + if (c->write_data_alloc < t) + c->write_data = pa_xrealloc(c->write_data, c->write_data_alloc = t); + + assert(c->write_data); + + i = c->write_data_length; + c->write_data_length += length; + + return (uint8_t*) c->write_data+i; +} + +static void format_esd2native(int format, int swap_bytes, pa_sample_spec *ss) { + assert(ss); + + ss->channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1; + if ((format & ESD_MASK_BITS) == ESD_BITS16) + ss->format = swap_bytes ? PA_SAMPLE_S16RE : PA_SAMPLE_S16NE; + else + ss->format = PA_SAMPLE_U8; +} + +static int format_native2esd(pa_sample_spec *ss) { + int format = 0; + + format = (ss->format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16; + format |= (ss->channels >= 2) ? ESD_STEREO : ESD_MONO; + + return format; +} + +/*** esound commands ***/ + +static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { + uint32_t ekey; + int *ok; + assert(length == (ESD_KEY_LEN + sizeof(uint32_t))); + + if (!c->authorized) { + if (memcmp(data, c->protocol->esd_key, ESD_KEY_LEN) != 0) { + pa_log(__FILE__": kicked client with invalid authorization key.\n"); + return -1; + } + + c->authorized = 1; + if (c->auth_timeout_event) { + c->protocol->core->mainloop->time_free(c->auth_timeout_event); + c->auth_timeout_event = NULL; + } + } + + ekey = *(const uint32_t*)((const uint8_t*) data+ESD_KEY_LEN); + if (ekey == ESD_ENDIAN_KEY) + c->swap_byte_order = 0; + else if (ekey == ESD_SWAP_ENDIAN_KEY) + c->swap_byte_order = 1; + else { + pa_log(__FILE__": client sent invalid endian key\n"); + return -1; + } + + ok = connection_write(c, sizeof(int)); + assert(ok); + *ok = 1; + return 0; +} + +static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { + char name[ESD_NAME_MAX]; + int format, rate; + pa_sink *sink; + pa_sample_spec ss; + size_t l; + assert(c && length == (sizeof(int)*2+ESD_NAME_MAX)); + + format = MAYBE_INT32_SWAP(c->swap_byte_order, *(const int*)data); + rate = MAYBE_INT32_SWAP(c->swap_byte_order, *((const int*)data + 1)); + + ss.rate = rate; + format_esd2native(format, c->swap_byte_order, &ss); + + if (!pa_sample_spec_valid(&ss)) { + pa_log(__FILE__": invalid sample specification\n"); + return -1; + } + + if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) { + pa_log(__FILE__": no such sink\n"); + return -1; + } + + strncpy(name, (const char*) data + sizeof(int)*2, sizeof(name)); + name[sizeof(name)-1] = 0; + + pa_client_set_name(c->client, name); + + assert(!c->sink_input && !c->input_memblockq); + + if (!(c->sink_input = pa_sink_input_new(sink, __FILE__, name, &ss, NULL, 0, -1))) { + pa_log(__FILE__": failed to create sink input.\n"); + return -1; + } + + l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS); + c->input_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&ss), l/2, l/PLAYBACK_BUFFER_FRAGMENTS, c->protocol->core->memblock_stat); + pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2); + c->playback.fragment_size = l/10; + + c->sink_input->owner = c->protocol->module; + c->sink_input->client = c->client; + c->sink_input->peek = sink_input_peek_cb; + c->sink_input->drop = sink_input_drop_cb; + c->sink_input->kill = sink_input_kill_cb; + c->sink_input->get_latency = sink_input_get_latency_cb; + c->sink_input->userdata = c; + + c->state = ESD_STREAMING_DATA; + + c->protocol->n_player++; + + return 0; +} + +static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length) { + char name[ESD_NAME_MAX]; + int format, rate; + pa_source *source; + pa_sample_spec ss; + size_t l; + assert(c && length == (sizeof(int)*2+ESD_NAME_MAX)); + + format = MAYBE_INT32_SWAP(c->swap_byte_order, *(const int*)data); + rate = MAYBE_INT32_SWAP(c->swap_byte_order, *((const int*)data + 1)); + + ss.rate = rate; + format_esd2native(format, c->swap_byte_order, &ss); + + if (!pa_sample_spec_valid(&ss)) { + pa_log(__FILE__": invalid sample specification.\n"); + return -1; + } + + if (request == ESD_PROTO_STREAM_MON) { + pa_sink* sink; + + if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) { + pa_log(__FILE__": no such sink.\n"); + return -1; + } + + if (!(source = sink->monitor_source)) { + pa_log(__FILE__": no such monitor source.\n"); + return -1; + } + } else { + assert(request == ESD_PROTO_STREAM_REC); + + if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, 1))) { + pa_log(__FILE__": no such source.\n"); + return -1; + } + } + + strncpy(name, (const char*) data + sizeof(int)*2, sizeof(name)); + name[sizeof(name)-1] = 0; + + pa_client_set_name(c->client, name); + + assert(!c->output_memblockq && !c->source_output); + + if (!(c->source_output = pa_source_output_new(source, __FILE__, name, &ss, NULL, -1))) { + pa_log(__FILE__": failed to create source output\n"); + return -1; + } + + l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS); + c->output_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&ss), 0, 0, c->protocol->core->memblock_stat); + pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2); + + c->source_output->owner = c->protocol->module; + c->source_output->client = c->client; + c->source_output->push = source_output_push_cb; + c->source_output->kill = source_output_kill_cb; + c->source_output->get_latency = source_output_get_latency_cb; + c->source_output->userdata = c; + + c->state = ESD_STREAMING_DATA; + + c->protocol->n_player++; + + return 0; +} + +static int esd_proto_get_latency(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { + pa_sink *sink; + int latency, *lag; + assert(c && !data && length == 0); + + if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) + latency = 0; + else { + double usec = pa_sink_get_latency(sink); + latency = (int) ((usec*44100)/1000000); + } + + lag = connection_write(c, sizeof(int)); + assert(lag); + *lag = MAYBE_INT32_SWAP(c->swap_byte_order, latency); + return 0; +} + +static int esd_proto_server_info(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { + int rate = 44100, format = ESD_STEREO|ESD_BITS16; + int *response; + pa_sink *sink; + assert(c && data && length == sizeof(int)); + + if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) { + rate = sink->sample_spec.rate; + format = format_native2esd(&sink->sample_spec); + } + + response = connection_write(c, sizeof(int)*3); + assert(response); + *(response++) = 0; + *(response++) = MAYBE_INT32_SWAP(c->swap_byte_order, rate); + *(response++) = MAYBE_INT32_SWAP(c->swap_byte_order, format); + return 0; +} + +static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length) { + uint8_t *response; + size_t t, k, s; + struct connection *conn; + size_t idx = PA_IDXSET_INVALID; + unsigned nsamples; + assert(c && data && length == sizeof(int)); + + if (esd_proto_server_info(c, request, data, length) < 0) + return -1; + + k = sizeof(int)*5+ESD_NAME_MAX; + s = sizeof(int)*6+ESD_NAME_MAX; + nsamples = c->protocol->core->scache ? pa_idxset_size(c->protocol->core->scache) : 0; + response = connection_write(c, (t = s*(nsamples+1) + k*(c->protocol->n_player+1))); + assert(k); + + for (conn = pa_idxset_first(c->protocol->connections, &idx); conn; conn = pa_idxset_next(c->protocol->connections, &idx)) { + int format = ESD_BITS16 | ESD_STEREO, rate = 44100, lvolume = ESD_VOLUME_BASE, rvolume = ESD_VOLUME_BASE; + + if (conn->state != ESD_STREAMING_DATA) + continue; + + assert(t >= s+k+k); + + if (conn->sink_input) { + rate = conn->sink_input->sample_spec.rate; + lvolume = (conn->sink_input->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM; + rvolume = (conn->sink_input->volume.values[1]*ESD_VOLUME_BASE)/PA_VOLUME_NORM; + format = format_native2esd(&conn->sink_input->sample_spec); + } + + /* id */ + *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (int) (conn->index+1)); + response += sizeof(int); + + /* name */ + assert(conn->client); + strncpy((char*) response, conn->client->name, ESD_NAME_MAX); + response += ESD_NAME_MAX; + + /* rate */ + *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, rate); + response += sizeof(int); + + /* left */ + *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, lvolume); + response += sizeof(int); + + /*right*/ + *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, rvolume); + response += sizeof(int); + + /*format*/ + *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, format); + response += sizeof(int); + + t-= k; + } + + assert(t == s*(nsamples+1)+k); + memset(response, 0, k); + response += k; + t -= k; + + if (nsamples) { + pa_scache_entry *ce; + + idx = PA_IDXSET_INVALID; + for (ce = pa_idxset_first(c->protocol->core->scache, &idx); ce; ce = pa_idxset_next(c->protocol->core->scache, &idx)) { + assert(t >= s*2); + + /* id */ + *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1)); + response += sizeof(int); + + /* name */ + if (strncmp(ce->name, SCACHE_PREFIX, sizeof(SCACHE_PREFIX)-1) == 0) + strncpy((char*) response, ce->name+sizeof(SCACHE_PREFIX)-1, ESD_NAME_MAX); + else + snprintf((char*) response, ESD_NAME_MAX, "native.%s", ce->name); + response += ESD_NAME_MAX; + + /* rate */ + *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, ce->sample_spec.rate); + response += sizeof(int); + + /* left */ + *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM); + response += sizeof(int); + + /*right*/ + *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM); + response += sizeof(int); + + /*format*/ + *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ce->sample_spec)); + response += sizeof(int); + + /*length*/ + *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (int) ce->memchunk.length); + response += sizeof(int); + + t -= s; + } + } + + assert(t == s); + memset(response, 0, s); + + return 0; +} + +static int esd_proto_stream_pan(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { + int *ok; + uint32_t idx; + pa_volume_t lvolume, rvolume; + struct connection *conn; + assert(c && data && length == sizeof(int)*3); + + idx = MAYBE_UINT32_SWAP(c->swap_byte_order, *(const int*)data)-1; + lvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, *((const int*)data + 1)); + lvolume = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE; + rvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, *((const int*)data + 2)); + rvolume = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE; + + ok = connection_write(c, sizeof(int)); + assert(ok); + + if ((conn = pa_idxset_get_by_index(c->protocol->connections, idx))) { + assert(conn->sink_input); + conn->sink_input->volume.values[0] = lvolume; + conn->sink_input->volume.values[1] = rvolume; + conn->sink_input->volume.channels = 2; + *ok = 1; + } else + *ok = 0; + + return 0; +} + +static int esd_proto_sample_cache(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { + pa_sample_spec ss; + int format, rate; + size_t sc_length; + uint32_t idx; + int *ok; + char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1]; + assert(c && data && length == (ESD_NAME_MAX+3*sizeof(int))); + + format = MAYBE_INT32_SWAP(c->swap_byte_order, *(const int*)data); + rate = MAYBE_INT32_SWAP(c->swap_byte_order, *((const int*)data + 1)); + + ss.rate = rate; + format_esd2native(format, c->swap_byte_order, &ss); + + sc_length = (size_t) MAYBE_INT32_SWAP(c->swap_byte_order, (*((const int*)data + 2))); + + if (sc_length >= MAX_CACHE_SAMPLE_SIZE) + return -1; + + strcpy(name, SCACHE_PREFIX); + strncpy(name+sizeof(SCACHE_PREFIX)-1, (const char*) data+3*sizeof(int), ESD_NAME_MAX); + name[sizeof(name)-1] = 0; + + assert(!c->scache.memchunk.memblock); + c->scache.memchunk.memblock = pa_memblock_new(sc_length, c->protocol->core->memblock_stat); + c->scache.memchunk.index = 0; + c->scache.memchunk.length = sc_length; + c->scache.sample_spec = ss; + assert(!c->scache.name); + c->scache.name = pa_xstrdup(name); + + c->state = ESD_CACHING_SAMPLE; + + pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, &idx); + + ok = connection_write(c, sizeof(int)); + assert(ok); + + *ok = idx+1; + + return 0; +} + +static int esd_proto_sample_get_id(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) { + int *ok; + uint32_t idx; + char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1]; + assert(c && data && length == ESD_NAME_MAX); + + ok = connection_write(c, sizeof(int)); + assert(ok); + + *ok = -1; + + strcpy(name, SCACHE_PREFIX); + strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX); + name[sizeof(name)-1] = 0; + + if ((idx = pa_scache_get_id_by_name(c->protocol->core, name)) != PA_IDXSET_INVALID) + *ok = (int) idx +1; + + return 0; +} + +static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length) { + int *ok; + const char *name; + uint32_t idx; + assert(c && data && length == sizeof(int)); + + idx = (uint32_t) MAYBE_INT32_SWAP(c->swap_byte_order, *(const int*)data)-1; + + ok = connection_write(c, sizeof(int)); + assert(ok); + + *ok = 0; + + if ((name = pa_scache_get_name_by_id(c->protocol->core, idx))) { + if (request == ESD_PROTO_SAMPLE_PLAY) { + pa_sink *sink; + + if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) + if (pa_scache_play_item(c->protocol->core, name, sink, NULL) >= 0) + *ok = (int) idx+1; + } else { + assert(request == ESD_PROTO_SAMPLE_FREE); + + if (pa_scache_remove_item(c->protocol->core, name) >= 0) + *ok = (int) idx+1; + } + } + + return 0; +} + +static int esd_proto_standby_or_resume(struct connection *c, PA_GCC_UNUSED esd_proto_t request, PA_GCC_UNUSED const void *data, PA_GCC_UNUSED size_t length) { + int *ok; + ok = connection_write(c, sizeof(int)*2); + assert(ok); + ok[0] = 1; + ok[1] = 1; + return 0; +} + +/*** client callbacks ***/ + +static void client_kill_cb(pa_client *c) { + assert(c && c->userdata); + connection_free(c->userdata); +} + +/*** pa_iochannel callbacks ***/ + +static int do_read(struct connection *c) { + assert(c && c->io); + +/* pa_log("READ\n"); */ + + if (c->state == ESD_NEXT_REQUEST) { + ssize_t r; + assert(c->read_data_length < sizeof(c->request)); + + if ((r = pa_iochannel_read(c->io, ((uint8_t*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) { + if (r != 0) + pa_log_warn(__FILE__": read() failed: %s\n", strerror(errno)); + return -1; + } + + if ((c->read_data_length+= r) >= sizeof(c->request)) { + struct proto_handler *handler; + + c->request = MAYBE_INT32_SWAP(c->swap_byte_order, c->request); + + if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) { + pa_log(__FILE__": recieved invalid request.\n"); + return -1; + } + + handler = proto_map+c->request; + +/* pa_log(__FILE__": executing request #%u\n", c->request); */ + + if (!handler->proc) { + pa_log(__FILE__": recieved unimplemented request #%u.\n", c->request); + return -1; + } + + if (handler->data_length == 0) { + c->read_data_length = 0; + + if (handler->proc(c, c->request, NULL, 0) < 0) + return -1; + + } else { + if (c->read_data_alloc < handler->data_length) + c->read_data = pa_xrealloc(c->read_data, c->read_data_alloc = handler->data_length); + assert(c->read_data); + + c->state = ESD_NEEDS_REQDATA; + c->read_data_length = 0; + } + } + + } else if (c->state == ESD_NEEDS_REQDATA) { + ssize_t r; + struct proto_handler *handler = proto_map+c->request; + + assert(handler->proc); + + assert(c->read_data && c->read_data_length < handler->data_length); + + if ((r = pa_iochannel_read(c->io, (uint8_t*) c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) { + if (r != 0) + pa_log_warn(__FILE__": read() failed: %s\n", strerror(errno)); + return -1; + } + + if ((c->read_data_length+= r) >= handler->data_length) { + size_t l = c->read_data_length; + assert(handler->proc); + + c->state = ESD_NEXT_REQUEST; + c->read_data_length = 0; + + if (handler->proc(c, c->request, c->read_data, l) < 0) + return -1; + } + } else if (c->state == ESD_CACHING_SAMPLE) { + ssize_t r; + + assert(c->scache.memchunk.memblock && c->scache.name && c->scache.memchunk.index < c->scache.memchunk.length); + + if ((r = pa_iochannel_read(c->io, (uint8_t*) c->scache.memchunk.memblock->data+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index)) <= 0) { + if (r!= 0) + pa_log_warn(__FILE__": read() failed: %s\n", strerror(errno)); + return -1; + } + + c->scache.memchunk.index += r; + assert(c->scache.memchunk.index <= c->scache.memchunk.length); + + if (c->scache.memchunk.index == c->scache.memchunk.length) { + uint32_t idx; + int *ok; + + c->scache.memchunk.index = 0; + pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, &idx); + + pa_memblock_unref(c->scache.memchunk.memblock); + c->scache.memchunk.memblock = NULL; + c->scache.memchunk.index = c->scache.memchunk.length = 0; + + pa_xfree(c->scache.name); + c->scache.name = NULL; + + c->state = ESD_NEXT_REQUEST; + + ok = connection_write(c, sizeof(int)); + assert(ok); + *ok = idx+1; + } + + } else if (c->state == ESD_STREAMING_DATA && c->sink_input) { + pa_memchunk chunk; + ssize_t r; + size_t l; + + assert(c->input_memblockq); + +/* pa_log("STREAMING_DATA\n"); */ + + if (!(l = pa_memblockq_missing(c->input_memblockq))) + return 0; + + if (l > c->playback.fragment_size) + l = c->playback.fragment_size; + + if (c->playback.current_memblock) + if (c->playback.current_memblock->length - c->playback.memblock_index < l) { + pa_memblock_unref(c->playback.current_memblock); + c->playback.current_memblock = NULL; + c->playback.memblock_index = 0; + } + + if (!c->playback.current_memblock) { + c->playback.current_memblock = pa_memblock_new(c->playback.fragment_size*2, c->protocol->core->memblock_stat); + assert(c->playback.current_memblock && c->playback.current_memblock->length >= l); + c->playback.memblock_index = 0; + } + + if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) { + if (r != 0) + pa_log(__FILE__": read() failed: %s\n", strerror(errno)); + return -1; + } + +/* pa_log(__FILE__": read %u\n", r); */ + + chunk.memblock = c->playback.current_memblock; + chunk.index = c->playback.memblock_index; + chunk.length = r; + assert(chunk.memblock); + + c->playback.memblock_index += r; + + assert(c->input_memblockq); + pa_memblockq_push_align(c->input_memblockq, &chunk, 0); + assert(c->sink_input); + pa_sink_notify(c->sink_input->sink); + } + + return 0; +} + +static int do_write(struct connection *c) { + assert(c && c->io); + +/* pa_log("WRITE\n"); */ + + if (c->write_data_length) { + ssize_t r; + + assert(c->write_data_index < c->write_data_length); + if ((r = pa_iochannel_write(c->io, (uint8_t*) c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) { + pa_log(__FILE__": write() failed: %s\n", strerror(errno)); + return -1; + } + + if ((c->write_data_index +=r) >= c->write_data_length) + c->write_data_length = c->write_data_index = 0; + + } else if (c->state == ESD_STREAMING_DATA && c->source_output) { + pa_memchunk chunk; + ssize_t r; + + assert(c->output_memblockq); + if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) + return 0; + + assert(chunk.memblock && chunk.length); + + if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) { + pa_memblock_unref(chunk.memblock); + pa_log(__FILE__": write(): %s\n", strerror(errno)); + return -1; + } + + pa_memblockq_drop(c->output_memblockq, &chunk, r); + pa_memblock_unref(chunk.memblock); + } + + return 0; +} + +static void do_work(struct connection *c) { + assert(c); + + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); + c->protocol->core->mainloop->defer_enable(c->defer_event, 0); + +/* pa_log("DOWORK %i\n", pa_iochannel_is_hungup(c->io)); */ + + if (!c->dead && pa_iochannel_is_readable(c->io)) + if (do_read(c) < 0) + goto fail; + + if (!c->dead && pa_iochannel_is_writable(c->io)) + if (do_write(c) < 0) + goto fail; + + /* In case the line was hungup, make sure to rerun this function + as soon as possible, until all data has been read. */ + + if (!c->dead && pa_iochannel_is_hungup(c->io)) + c->protocol->core->mainloop->defer_enable(c->defer_event, 1); + + return; + +fail: + + if (c->state == ESD_STREAMING_DATA && c->sink_input) { + c->dead = 1; + pa_memblockq_prebuf_disable(c->input_memblockq); + + pa_iochannel_free(c->io); + c->io = NULL; + + } else + connection_free(c); +} + +static void io_callback(pa_iochannel*io, void *userdata) { + struct connection *c = userdata; + assert(io && c && c->io == io); + +/* pa_log("IO\n"); */ + + do_work(c); +} + +/*** defer callback ***/ + +static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) { + struct connection *c = userdata; + assert(a && c && c->defer_event == e); + +/* pa_log("DEFER\n"); */ + + do_work(c); +} + +/*** sink_input callbacks ***/ + +static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) { + struct connection*c; + assert(i && i->userdata && chunk); + c = i->userdata; + + if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) { + + if (c->dead) + connection_free(c); + + return -1; + } + + return 0; +} + +static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { + struct connection*c = i->userdata; + assert(i && c && length); + +/* pa_log("DROP\n"); */ + + pa_memblockq_drop(c->input_memblockq, chunk, length); + + /* do something */ + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); + + if (!c->dead) + c->protocol->core->mainloop->defer_enable(c->defer_event, 1); + +/* assert(pa_memblockq_get_length(c->input_memblockq) > 2048); */ +} + +static void sink_input_kill_cb(pa_sink_input *i) { + assert(i && i->userdata); + connection_free((struct connection *) i->userdata); +} + +static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) { + struct connection*c = i->userdata; + assert(i && c); + return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); +} + +/*** source_output callbacks ***/ + +static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { + struct connection *c = o->userdata; + assert(o && c && chunk); + + pa_memblockq_push(c->output_memblockq, chunk, 0); + + /* do something */ + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); + + if (!c->dead) + c->protocol->core->mainloop->defer_enable(c->defer_event, 1); +} + +static void source_output_kill_cb(pa_source_output *o) { + assert(o && o->userdata); + connection_free((struct connection *) o->userdata); +} + +static pa_usec_t source_output_get_latency_cb(pa_source_output *o) { + struct connection*c = o->userdata; + assert(o && c); + return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec); +} + +/*** socket server callback ***/ + +static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) { + struct connection *c = userdata; + assert(m && tv && c && c->auth_timeout_event == e); + + if (!c->authorized) + connection_free(c); +} + +static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) { + struct connection *c; + pa_protocol_esound *p = userdata; + char cname[256]; + assert(s && io && p); + + if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) { + pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS); + pa_iochannel_free(io); + return; + } + + c = pa_xmalloc(sizeof(struct connection)); + c->protocol = p; + c->io = io; + pa_iochannel_set_callback(c->io, io_callback, c); + + pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); + assert(p->core); + c->client = pa_client_new(p->core, __FILE__, cname); + assert(c->client); + c->client->owner = p->module; + c->client->kill = client_kill_cb; + c->client->userdata = c; + + c->authorized = p->public; + c->swap_byte_order = 0; + c->dead = 0; + + c->read_data_length = 0; + c->read_data = pa_xmalloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length); + + c->write_data_length = c->write_data_index = c->write_data_alloc = 0; + c->write_data = NULL; + + c->state = ESD_NEEDS_REQDATA; + c->request = ESD_PROTO_CONNECT; + + c->sink_input = NULL; + c->input_memblockq = NULL; + + c->source_output = NULL; + c->output_memblockq = NULL; + + c->playback.current_memblock = NULL; + c->playback.memblock_index = 0; + c->playback.fragment_size = 0; + + c->scache.memchunk.length = c->scache.memchunk.index = 0; + c->scache.memchunk.memblock = NULL; + c->scache.name = NULL; + + if (!c->authorized) { + struct timeval tv; + pa_gettimeofday(&tv); + tv.tv_sec += AUTH_TIMEOUT; + c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c); + } else + c->auth_timeout_event = NULL; + + c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c); + assert(c->defer_event); + p->core->mainloop->defer_enable(c->defer_event, 0); + + pa_idxset_put(p->connections, c, &c->index); +} + +/*** entry points ***/ + +pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma) { + pa_protocol_esound *p; + int public = 0; + assert(core && server && ma); + + p = pa_xmalloc(sizeof(pa_protocol_esound)); + + if (pa_modargs_get_value_boolean(ma, "public", &public) < 0) { + pa_log(__FILE__": public= expects a boolean argument.\n"); + return NULL; + } + + if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", DEFAULT_COOKIE_FILE), p->esd_key, sizeof(p->esd_key)) < 0) { + pa_xfree(p); + return NULL; + } + + p->module = m; + p->public = public; + p->server = server; + pa_socket_server_set_callback(p->server, on_connection, p); + p->core = core; + p->connections = pa_idxset_new(NULL, NULL); + assert(p->connections); + + p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); + p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL)); + p->n_player = 0; + + return p; +} + +void pa_protocol_esound_free(pa_protocol_esound *p) { + struct connection *c; + assert(p); + + while ((c = pa_idxset_first(p->connections, NULL))) + connection_free(c); + + pa_idxset_free(p->connections, NULL, NULL); + pa_socket_server_unref(p->server); + pa_xfree(p); +} diff --git a/src/polypcore/protocol-esound.h b/src/polypcore/protocol-esound.h new file mode 100644 index 00000000..71d58464 --- /dev/null +++ b/src/polypcore/protocol-esound.h @@ -0,0 +1,35 @@ +#ifndef fooprotocolesoundhfoo +#define fooprotocolesoundhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "core.h" +#include "socket-server.h" +#include "module.h" +#include "modargs.h" + +typedef struct pa_protocol_esound pa_protocol_esound; + +pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma); +void pa_protocol_esound_free(pa_protocol_esound *p); + +#endif diff --git a/src/polypcore/protocol-http.c b/src/polypcore/protocol-http.c new file mode 100644 index 00000000..3e55df03 --- /dev/null +++ b/src/polypcore/protocol-http.c @@ -0,0 +1,265 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "protocol-http.h" +#include "ioline.h" +#include "xmalloc.h" +#include "log.h" +#include "namereg.h" +#include "cli-text.h" + +/* Don't allow more than this many concurrent connections */ +#define MAX_CONNECTIONS 10 + +#define internal_server_error(c) http_message((c), 500, "Internal Server Error", NULL) + +#define URL_ROOT "/" +#define URL_CSS "/style" +#define URL_STATUS "/status" + +struct connection { + pa_protocol_http *protocol; + pa_ioline *line; + enum { REQUEST_LINE, MIME_HEADER, DATA } state; + char *url; +}; + +struct pa_protocol_http { + pa_module *module; + pa_core *core; + pa_socket_server*server; + pa_idxset *connections; +}; + +static void http_response(struct connection *c, int code, const char *msg, const char *mime) { + char s[256]; + assert(c); + assert(msg); + assert(mime); + + snprintf(s, sizeof(s), + "HTTP/1.0 %i %s\n" + "Connection: close\n" + "Content-Type: %s\n" + "Cache-Control: no-cache\n" + "Expires: 0\n" + "Server: "PACKAGE_NAME"/"PACKAGE_VERSION"\n" + "\n", code, msg, mime); + + pa_ioline_puts(c->line, s); +} + +static void http_message(struct connection *c, int code, const char *msg, const char *text) { + char s[256]; + assert(c); + + http_response(c, code, msg, "text/html"); + + if (!text) + text = msg; + + snprintf(s, sizeof(s), + "\n" + "\n" + "%s\n" + "%s\n", + text, text); + + pa_ioline_puts(c->line, s); + pa_ioline_defer_close(c->line); +} + + +static void connection_free(struct connection *c, int del) { + assert(c); + + if (c->url) + pa_xfree(c->url); + + if (del) + pa_idxset_remove_by_data(c->protocol->connections, c, NULL); + pa_ioline_unref(c->line); + pa_xfree(c); +} + +static void line_callback(pa_ioline *line, const char *s, void *userdata) { + struct connection *c = userdata; + assert(line); + assert(c); + + if (!s) { + /* EOF */ + connection_free(c, 1); + return; + } + + switch (c->state) { + case REQUEST_LINE: { + if (memcmp(s, "GET ", 4)) + goto fail; + + s +=4; + + c->url = pa_xstrndup(s, strcspn(s, " \r\n\t?")); + c->state = MIME_HEADER; + break; + + } + + case MIME_HEADER: { + + /* Ignore MIME headers */ + if (strcspn(s, " \r\n") != 0) + break; + + /* We're done */ + c->state = DATA; + + pa_log_info(__FILE__": request for %s\n", c->url); + + if (!strcmp(c->url, URL_ROOT)) { + char txt[256]; + http_response(c, 200, "OK", "text/html"); + + pa_ioline_puts(c->line, + "\n" + "\n" + ""PACKAGE_NAME" "PACKAGE_VERSION"\n" + "\n"); + + pa_ioline_puts(c->line, + "

"PACKAGE_NAME" "PACKAGE_VERSION"

\n" + ""); + +#define PRINTF_FIELD(a,b) pa_ioline_printf(c->line, "\n",(a),(b)) + + PRINTF_FIELD("User Name:", pa_get_user_name(txt, sizeof(txt))); + PRINTF_FIELD("Fully Qualified Domain Name:", pa_get_fqdn(txt, sizeof(txt))); + PRINTF_FIELD("Default Sample Specification:", pa_sample_spec_snprint(txt, sizeof(txt), &c->protocol->core->default_sample_spec)); + PRINTF_FIELD("Default Sink:", pa_namereg_get_default_sink_name(c->protocol->core)); + PRINTF_FIELD("Default Source:", pa_namereg_get_default_source_name(c->protocol->core)); + + pa_ioline_puts(c->line, "
%s%s
"); + + pa_ioline_puts(c->line, "

Click here for an extensive server status report.

"); + + pa_ioline_puts(c->line, "\n"); + + pa_ioline_defer_close(c->line); + } else if (!strcmp(c->url, URL_CSS)) { + http_response(c, 200, "OK", "text/css"); + + pa_ioline_puts(c->line, + "body { color: black; background-color: white; margin: 0.5cm; }\n" + "a:link, a:visited { color: #900000; }\n" + "p { margin-left: 0.5cm; margin-right: 0.5cm; }\n" + "h1 { color: #00009F; }\n" + "h2 { color: #00009F; }\n" + "ul { margin-left: .5cm; }\n" + "ol { margin-left: .5cm; }\n" + "pre { margin-left: .5cm; background-color: #f0f0f0; padding: 0.4cm;}\n" + ".grey { color: #afafaf; }\n" + "table { margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; }\n" + "td { padding-left:10px; padding-right:10px; }\n"); + + pa_ioline_defer_close(c->line); + } else if (!strcmp(c->url, URL_STATUS)) { + char *r; + + http_response(c, 200, "OK", "text/plain"); + r = pa_full_status_string(c->protocol->core); + pa_ioline_puts(c->line, r); + pa_xfree(r); + + pa_ioline_defer_close(c->line); + } else + http_message(c, 404, "Not Found", NULL); + + break; + } + + default: + ; + } + + return; + +fail: + internal_server_error(c); +} + +static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) { + pa_protocol_http *p = userdata; + struct connection *c; + assert(s && io && p); + + if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) { + pa_log_warn(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS); + pa_iochannel_free(io); + return; + } + + c = pa_xmalloc(sizeof(struct connection)); + c->protocol = p; + c->line = pa_ioline_new(io); + c->state = REQUEST_LINE; + c->url = NULL; + + pa_ioline_set_callback(c->line, line_callback, c); + pa_idxset_put(p->connections, c, NULL); +} + +pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, pa_module *m, PA_GCC_UNUSED pa_modargs *ma) { + pa_protocol_http* p; + assert(core && server); + + p = pa_xmalloc(sizeof(pa_protocol_http)); + p->module = m; + p->core = core; + p->server = server; + p->connections = pa_idxset_new(NULL, NULL); + + pa_socket_server_set_callback(p->server, on_connection, p); + + return p; +} + +static void free_connection(void *p, PA_GCC_UNUSED void *userdata) { + assert(p); + connection_free(p, 0); +} + +void pa_protocol_http_free(pa_protocol_http *p) { + assert(p); + + pa_idxset_free(p->connections, free_connection, NULL); + pa_socket_server_unref(p->server); + pa_xfree(p); +} diff --git a/src/polypcore/protocol-http.h b/src/polypcore/protocol-http.h new file mode 100644 index 00000000..0a1855e9 --- /dev/null +++ b/src/polypcore/protocol-http.h @@ -0,0 +1,35 @@ +#ifndef fooprotocolhttphfoo +#define fooprotocolhttphfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "core.h" +#include "socket-server.h" +#include "module.h" +#include "modargs.h" + +typedef struct pa_protocol_http pa_protocol_http; + +pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma); +void pa_protocol_http_free(pa_protocol_http *n); + +#endif diff --git a/src/polypcore/protocol-native.c b/src/polypcore/protocol-native.c new file mode 100644 index 00000000..b94903d9 --- /dev/null +++ b/src/polypcore/protocol-native.c @@ -0,0 +1,2216 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "protocol-native.h" +#include "native-common.h" +#include "packet.h" +#include "client.h" +#include "source-output.h" +#include "sink-input.h" +#include "pstream.h" +#include "tagstruct.h" +#include "pdispatch.h" +#include "pstream-util.h" +#include "authkey.h" +#include "namereg.h" +#include "scache.h" +#include "xmalloc.h" +#include "util.h" +#include "subscribe.h" +#include "log.h" +#include "autoload.h" +#include "authkey-prop.h" +#include "strlist.h" +#include "props.h" + +/* Kick a client if it doesn't authenticate within this time */ +#define AUTH_TIMEOUT 5 + +/* Don't accept more connection than this */ +#define MAX_CONNECTIONS 10 + +struct connection; +struct pa_protocol_native; + +struct record_stream { + struct connection *connection; + uint32_t index; + pa_source_output *source_output; + pa_memblockq *memblockq; + size_t fragment_size; +}; + +struct playback_stream { + int type; + struct connection *connection; + uint32_t index; + pa_sink_input *sink_input; + pa_memblockq *memblockq; + size_t requested_bytes; + int drain_request; + uint32_t drain_tag; +}; + +struct upload_stream { + int type; + struct connection *connection; + uint32_t index; + pa_memchunk memchunk; + size_t length; + char *name; + pa_sample_spec sample_spec; + pa_channel_map channel_map; +}; + +struct output_stream { + int type; +}; + +enum { + UPLOAD_STREAM, + PLAYBACK_STREAM +}; + +struct connection { + int authorized; + pa_protocol_native *protocol; + pa_client *client; + pa_pstream *pstream; + pa_pdispatch *pdispatch; + pa_idxset *record_streams, *output_streams; + uint32_t rrobin_index; + pa_subscription *subscription; + pa_time_event *auth_timeout_event; +}; + +struct pa_protocol_native { + pa_module *module; + int public; + pa_core *core; + pa_socket_server *server; + pa_idxset *connections; + uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH]; + int auth_cookie_in_property; +}; + +static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk); +static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length); +static void sink_input_kill_cb(pa_sink_input *i); +static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i); + +static void request_bytes(struct playback_stream*s); + +static void source_output_kill_cb(pa_source_output *o); +static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk); +static pa_usec_t source_output_get_latency_cb(pa_source_output *o); + +static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_drain_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_lookup(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_stat(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_get_record_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_finish_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_play_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_remove_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_subscribe(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_set_volume(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_cork_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_flush_or_trigger_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_set_stream_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_load_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_add_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_remove_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_get_autoload_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_get_autoload_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); + +static const pa_pdispatch_callback command_table[PA_COMMAND_MAX] = { + [PA_COMMAND_ERROR] = NULL, + [PA_COMMAND_TIMEOUT] = NULL, + [PA_COMMAND_REPLY] = NULL, + [PA_COMMAND_CREATE_PLAYBACK_STREAM] = command_create_playback_stream, + [PA_COMMAND_DELETE_PLAYBACK_STREAM] = command_delete_stream, + [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = command_drain_playback_stream, + [PA_COMMAND_CREATE_RECORD_STREAM] = command_create_record_stream, + [PA_COMMAND_DELETE_RECORD_STREAM] = command_delete_stream, + [PA_COMMAND_AUTH] = command_auth, + [PA_COMMAND_REQUEST] = NULL, + [PA_COMMAND_EXIT] = command_exit, + [PA_COMMAND_SET_CLIENT_NAME] = command_set_client_name, + [PA_COMMAND_LOOKUP_SINK] = command_lookup, + [PA_COMMAND_LOOKUP_SOURCE] = command_lookup, + [PA_COMMAND_STAT] = command_stat, + [PA_COMMAND_GET_PLAYBACK_LATENCY] = command_get_playback_latency, + [PA_COMMAND_GET_RECORD_LATENCY] = command_get_record_latency, + [PA_COMMAND_CREATE_UPLOAD_STREAM] = command_create_upload_stream, + [PA_COMMAND_DELETE_UPLOAD_STREAM] = command_delete_stream, + [PA_COMMAND_FINISH_UPLOAD_STREAM] = command_finish_upload_stream, + [PA_COMMAND_PLAY_SAMPLE] = command_play_sample, + [PA_COMMAND_REMOVE_SAMPLE] = command_remove_sample, + [PA_COMMAND_GET_SINK_INFO] = command_get_info, + [PA_COMMAND_GET_SOURCE_INFO] = command_get_info, + [PA_COMMAND_GET_CLIENT_INFO] = command_get_info, + [PA_COMMAND_GET_MODULE_INFO] = command_get_info, + [PA_COMMAND_GET_SINK_INPUT_INFO] = command_get_info, + [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = command_get_info, + [PA_COMMAND_GET_SAMPLE_INFO] = command_get_info, + [PA_COMMAND_GET_SINK_INFO_LIST] = command_get_info_list, + [PA_COMMAND_GET_SOURCE_INFO_LIST] = command_get_info_list, + [PA_COMMAND_GET_MODULE_INFO_LIST] = command_get_info_list, + [PA_COMMAND_GET_CLIENT_INFO_LIST] = command_get_info_list, + [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = command_get_info_list, + [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = command_get_info_list, + [PA_COMMAND_GET_SAMPLE_INFO_LIST] = command_get_info_list, + [PA_COMMAND_GET_SERVER_INFO] = command_get_server_info, + [PA_COMMAND_SUBSCRIBE] = command_subscribe, + + [PA_COMMAND_SET_SINK_VOLUME] = command_set_volume, + [PA_COMMAND_SET_SINK_INPUT_VOLUME] = command_set_volume, + + [PA_COMMAND_CORK_PLAYBACK_STREAM] = command_cork_playback_stream, + [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = command_flush_or_trigger_playback_stream, + [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = command_flush_or_trigger_playback_stream, + [PA_COMMAND_PREBUF_PLAYBACK_STREAM] = command_flush_or_trigger_playback_stream, + + [PA_COMMAND_CORK_RECORD_STREAM] = command_cork_record_stream, + [PA_COMMAND_FLUSH_RECORD_STREAM] = command_flush_record_stream, + + [PA_COMMAND_SET_DEFAULT_SINK] = command_set_default_sink_or_source, + [PA_COMMAND_SET_DEFAULT_SOURCE] = command_set_default_sink_or_source, + [PA_COMMAND_SET_PLAYBACK_STREAM_NAME] = command_set_stream_name, + [PA_COMMAND_SET_RECORD_STREAM_NAME] = command_set_stream_name, + [PA_COMMAND_KILL_CLIENT] = command_kill, + [PA_COMMAND_KILL_SINK_INPUT] = command_kill, + [PA_COMMAND_KILL_SOURCE_OUTPUT] = command_kill, + [PA_COMMAND_LOAD_MODULE] = command_load_module, + [PA_COMMAND_UNLOAD_MODULE] = command_unload_module, + [PA_COMMAND_GET_AUTOLOAD_INFO] = command_get_autoload_info, + [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = command_get_autoload_info_list, + [PA_COMMAND_ADD_AUTOLOAD] = command_add_autoload, + [PA_COMMAND_REMOVE_AUTOLOAD] = command_remove_autoload, + +}; + +/* structure management */ + +static struct upload_stream* upload_stream_new( + struct connection *c, + const pa_sample_spec *ss, + const pa_channel_map *map, + const char *name, size_t length) { + + struct upload_stream *s; + assert(c && ss && name && length); + + s = pa_xmalloc(sizeof(struct upload_stream)); + s->type = UPLOAD_STREAM; + s->connection = c; + s->sample_spec = *ss; + s->channel_map = *map; + s->name = pa_xstrdup(name); + + s->memchunk.memblock = NULL; + s->memchunk.index = 0; + s->memchunk.length = 0; + + s->length = length; + + pa_idxset_put(c->output_streams, s, &s->index); + return s; +} + +static void upload_stream_free(struct upload_stream *o) { + assert(o && o->connection); + + pa_idxset_remove_by_data(o->connection->output_streams, o, NULL); + + pa_xfree(o->name); + + if (o->memchunk.memblock) + pa_memblock_unref(o->memchunk.memblock); + + pa_xfree(o); +} + +static struct record_stream* record_stream_new( + struct connection *c, + pa_source *source, + const pa_sample_spec *ss, + const pa_channel_map *map, + const char *name, + size_t maxlength, + size_t fragment_size) { + + struct record_stream *s; + pa_source_output *source_output; + size_t base; + assert(c && source && ss && name && maxlength); + + if (!(source_output = pa_source_output_new(source, __FILE__, name, ss, map, -1))) + return NULL; + + s = pa_xmalloc(sizeof(struct record_stream)); + s->connection = c; + s->source_output = source_output; + s->source_output->push = source_output_push_cb; + s->source_output->kill = source_output_kill_cb; + s->source_output->get_latency = source_output_get_latency_cb; + s->source_output->userdata = s; + s->source_output->owner = c->protocol->module; + s->source_output->client = c->client; + + s->memblockq = pa_memblockq_new(maxlength, 0, base = pa_frame_size(ss), 0, 0, c->protocol->core->memblock_stat); + assert(s->memblockq); + + s->fragment_size = (fragment_size/base)*base; + if (!s->fragment_size) + s->fragment_size = base; + + pa_idxset_put(c->record_streams, s, &s->index); + return s; +} + +static void record_stream_free(struct record_stream* r) { + assert(r && r->connection); + + pa_idxset_remove_by_data(r->connection->record_streams, r, NULL); + pa_source_output_disconnect(r->source_output); + pa_source_output_unref(r->source_output); + pa_memblockq_free(r->memblockq); + pa_xfree(r); +} + +static struct playback_stream* playback_stream_new( + struct connection *c, + pa_sink *sink, + const pa_sample_spec *ss, + const pa_channel_map *map, + const char *name, + size_t maxlength, + size_t tlength, + size_t prebuf, + size_t minreq, + pa_cvolume *volume) { + + struct playback_stream *s; + pa_sink_input *sink_input; + assert(c && sink && ss && name && maxlength); + + if (!(sink_input = pa_sink_input_new(sink, __FILE__, name, ss, map, 0, -1))) + return NULL; + + s = pa_xmalloc(sizeof(struct playback_stream)); + s->type = PLAYBACK_STREAM; + s->connection = c; + s->sink_input = sink_input; + + s->sink_input->peek = sink_input_peek_cb; + s->sink_input->drop = sink_input_drop_cb; + s->sink_input->kill = sink_input_kill_cb; + s->sink_input->get_latency = sink_input_get_latency_cb; + s->sink_input->userdata = s; + s->sink_input->owner = c->protocol->module; + s->sink_input->client = c->client; + + s->memblockq = pa_memblockq_new(maxlength, tlength, pa_frame_size(ss), prebuf, minreq, c->protocol->core->memblock_stat); + assert(s->memblockq); + + s->requested_bytes = 0; + s->drain_request = 0; + + s->sink_input->volume = *volume; + + pa_idxset_put(c->output_streams, s, &s->index); + return s; +} + +static void playback_stream_free(struct playback_stream* p) { + assert(p && p->connection); + + if (p->drain_request) + pa_pstream_send_error(p->connection->pstream, p->drain_tag, PA_ERROR_NOENTITY); + + pa_idxset_remove_by_data(p->connection->output_streams, p, NULL); + pa_sink_input_disconnect(p->sink_input); + pa_sink_input_unref(p->sink_input); + pa_memblockq_free(p->memblockq); + pa_xfree(p); +} + +static void connection_free(struct connection *c) { + struct record_stream *r; + struct output_stream *o; + assert(c && c->protocol); + + pa_idxset_remove_by_data(c->protocol->connections, c, NULL); + while ((r = pa_idxset_first(c->record_streams, NULL))) + record_stream_free(r); + pa_idxset_free(c->record_streams, NULL, NULL); + + while ((o = pa_idxset_first(c->output_streams, NULL))) + if (o->type == PLAYBACK_STREAM) + playback_stream_free((struct playback_stream*) o); + else + upload_stream_free((struct upload_stream*) o); + pa_idxset_free(c->output_streams, NULL, NULL); + + pa_pdispatch_unref(c->pdispatch); + pa_pstream_close(c->pstream); + pa_pstream_unref(c->pstream); + pa_client_free(c->client); + + if (c->subscription) + pa_subscription_free(c->subscription); + + if (c->auth_timeout_event) + c->protocol->core->mainloop->time_free(c->auth_timeout_event); + + pa_xfree(c); +} + +static void request_bytes(struct playback_stream *s) { + pa_tagstruct *t; + size_t l; + assert(s); + + if (!(l = pa_memblockq_missing(s->memblockq))) + return; + + if (l <= s->requested_bytes) + return; + + l -= s->requested_bytes; + + if (l < pa_memblockq_get_minreq(s->memblockq)) + return; + + s->requested_bytes += l; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_REQUEST); + pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ + pa_tagstruct_putu32(t, s->index); + pa_tagstruct_putu32(t, l); + pa_pstream_send_tagstruct(s->connection->pstream, t); + +/* pa_log(__FILE__": Requesting %u bytes\n", l); */ +} + +static void send_memblock(struct connection *c) { + uint32_t start; + struct record_stream *r; + + start = PA_IDXSET_INVALID; + for (;;) { + pa_memchunk chunk; + + if (!(r = pa_idxset_rrobin(c->record_streams, &c->rrobin_index))) + return; + + if (start == PA_IDXSET_INVALID) + start = c->rrobin_index; + else if (start == c->rrobin_index) + return; + + if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) { + pa_memchunk schunk = chunk; + + if (schunk.length > r->fragment_size) + schunk.length = r->fragment_size; + + pa_pstream_send_memblock(c->pstream, r->index, 0, &schunk); + pa_memblockq_drop(r->memblockq, &chunk, schunk.length); + pa_memblock_unref(schunk.memblock); + + return; + } + } +} + +static void send_playback_stream_killed(struct playback_stream *p) { + pa_tagstruct *t; + assert(p); + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_KILLED); + pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ + pa_tagstruct_putu32(t, p->index); + pa_pstream_send_tagstruct(p->connection->pstream, t); +} + +static void send_record_stream_killed(struct record_stream *r) { + pa_tagstruct *t; + assert(r); + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_KILLED); + pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ + pa_tagstruct_putu32(t, r->index); + pa_pstream_send_tagstruct(r->connection->pstream, t); +} + +/*** sinkinput callbacks ***/ + +static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) { + struct playback_stream *s; + assert(i && i->userdata && chunk); + s = i->userdata; + + if (pa_memblockq_peek(s->memblockq, chunk) < 0) + return -1; + + return 0; +} + +static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { + struct playback_stream *s; + assert(i && i->userdata && length); + s = i->userdata; + + pa_memblockq_drop(s->memblockq, chunk, length); + request_bytes(s); + + if (s->drain_request && !pa_memblockq_is_readable(s->memblockq)) { + pa_pstream_send_simple_ack(s->connection->pstream, s->drain_tag); + s->drain_request = 0; + } + +/* pa_log(__FILE__": after_drop: %u\n", pa_memblockq_get_length(s->memblockq)); */ +} + +static void sink_input_kill_cb(pa_sink_input *i) { + assert(i && i->userdata); + send_playback_stream_killed((struct playback_stream *) i->userdata); + playback_stream_free((struct playback_stream *) i->userdata); +} + +static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) { + struct playback_stream *s; + assert(i && i->userdata); + s = i->userdata; + + /*pa_log(__FILE__": get_latency: %u\n", pa_memblockq_get_length(s->memblockq));*/ + + return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec); +} + +/*** source_output callbacks ***/ + +static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { + struct record_stream *s; + assert(o && o->userdata && chunk); + s = o->userdata; + + pa_memblockq_push_align(s->memblockq, chunk, 0); + if (!pa_pstream_is_pending(s->connection->pstream)) + send_memblock(s->connection); +} + +static void source_output_kill_cb(pa_source_output *o) { + assert(o && o->userdata); + send_record_stream_killed((struct record_stream *) o->userdata); + record_stream_free((struct record_stream *) o->userdata); +} + +static pa_usec_t source_output_get_latency_cb(pa_source_output *o) { + struct record_stream *s; + assert(o && o->userdata); + s = o->userdata; + + /*pa_log(__FILE__": get_latency: %u\n", pa_memblockq_get_length(s->memblockq));*/ + + return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec); +} + +/*** pdispatch callbacks ***/ + +static void protocol_error(struct connection *c) { + pa_log(__FILE__": protocol error, kicking client\n"); + connection_free(c); +} + +static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + struct playback_stream *s; + size_t maxlength, tlength, prebuf, minreq; + uint32_t sink_index; + const char *name, *sink_name; + pa_sample_spec ss; + pa_channel_map map; + pa_tagstruct *reply; + pa_sink *sink; + pa_cvolume volume; + int corked; + + assert(c && t && c->protocol && c->protocol->core); + + if (pa_tagstruct_get( + t, + PA_TAG_STRING, &name, + PA_TAG_SAMPLE_SPEC, &ss, + PA_TAG_CHANNEL_MAP, &map, + PA_TAG_U32, &sink_index, + PA_TAG_STRING, &sink_name, + PA_TAG_U32, &maxlength, + PA_TAG_BOOLEAN, &corked, + PA_TAG_U32, &tlength, + PA_TAG_U32, &prebuf, + PA_TAG_U32, &minreq, + PA_TAG_CVOLUME, &volume, + PA_TAG_INVALID) < 0 || + !pa_tagstruct_eof(t) || + !name) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (sink_index != (uint32_t) -1) + sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index); + else + sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1); + + if (!sink) { + pa_log("%s: Can't find a suitable sink.\n", __FILE__); + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + if (!(s = playback_stream_new(c, sink, &ss, &map, name, maxlength, tlength, prebuf, minreq, &volume))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID); + return; + } + + pa_sink_input_cork(s->sink_input, corked); + + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_putu32(reply, s->index); + assert(s->sink_input); + pa_tagstruct_putu32(reply, s->sink_input->index); + pa_tagstruct_putu32(reply, s->requested_bytes = pa_memblockq_missing(s->memblockq)); + pa_pstream_send_tagstruct(c->pstream, reply); + request_bytes(s); +} + +static void command_delete_stream(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t channel; + assert(c && t); + + if (pa_tagstruct_getu32(t, &channel) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (command == PA_COMMAND_DELETE_PLAYBACK_STREAM) { + struct playback_stream *s; + if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != PLAYBACK_STREAM)) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); + return; + } + + playback_stream_free(s); + } else if (command == PA_COMMAND_DELETE_RECORD_STREAM) { + struct record_stream *s; + if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); + return; + } + + record_stream_free(s); + } else { + struct upload_stream *s; + assert(command == PA_COMMAND_DELETE_UPLOAD_STREAM); + if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != UPLOAD_STREAM)) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); + return; + } + + upload_stream_free(s); + } + + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + struct record_stream *s; + size_t maxlength, fragment_size; + uint32_t source_index; + const char *name, *source_name; + pa_sample_spec ss; + pa_channel_map map; + pa_tagstruct *reply; + pa_source *source; + int corked; + assert(c && t && c->protocol && c->protocol->core); + + if (pa_tagstruct_gets(t, &name) < 0 || !name || + pa_tagstruct_get_sample_spec(t, &ss) < 0 || + pa_tagstruct_get_channel_map(t, &map) < 0 || + pa_tagstruct_getu32(t, &source_index) < 0 || + pa_tagstruct_gets(t, &source_name) < 0 || + pa_tagstruct_getu32(t, &maxlength) < 0 || + pa_tagstruct_get_boolean(t, &corked) < 0 || + pa_tagstruct_getu32(t, &fragment_size) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (source_index != (uint32_t) -1) + source = pa_idxset_get_by_index(c->protocol->core->sources, source_index); + else + source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE, 1); + + if (!source) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + if (!(s = record_stream_new(c, source, &ss, &map, name, maxlength, fragment_size))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID); + return; + } + + pa_source_output_cork(s->source_output, corked); + + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_putu32(reply, s->index); + assert(s->source_output); + pa_tagstruct_putu32(reply, s->source_output->index); + pa_pstream_send_tagstruct(c->pstream, reply); +} + +static void command_exit(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + assert(c && t); + + if (!pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop); + c->protocol->core->mainloop->quit(c->protocol->core->mainloop, 0); + pa_pstream_send_simple_ack(c->pstream, tag); /* nonsense */ + return; +} + +static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + const void*cookie; + assert(c && t); + + if (pa_tagstruct_get_arbitrary(t, &cookie, PA_NATIVE_COOKIE_LENGTH) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + if (memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) != 0) { + pa_log(__FILE__": Denied access to client with invalid authorization key.\n"); + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + c->authorized = 1; + if (c->auth_timeout_event) { + c->protocol->core->mainloop->time_free(c->auth_timeout_event); + c->auth_timeout_event = NULL; + } + } + + pa_pstream_send_simple_ack(c->pstream, tag); + return; +} + +static void command_set_client_name(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + const char *name; + assert(c && t); + + if (pa_tagstruct_gets(t, &name) < 0 || !name || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + pa_client_set_name(c->client, name); + pa_pstream_send_simple_ack(c->pstream, tag); + return; +} + +static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + const char *name; + uint32_t idx = PA_IDXSET_INVALID; + assert(c && t); + + if (pa_tagstruct_gets(t, &name) < 0 || !name || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (command == PA_COMMAND_LOOKUP_SINK) { + pa_sink *sink; + if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1))) + idx = sink->index; + } else { + pa_source *source; + assert(command == PA_COMMAND_LOOKUP_SOURCE); + if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1))) + idx = source->index; + } + + if (idx == PA_IDXSET_INVALID) + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + else { + pa_tagstruct *reply; + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_putu32(reply, idx); + pa_pstream_send_tagstruct(c->pstream, reply); + } +} + +static void command_drain_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t idx; + struct playback_stream *s; + assert(c && t); + + if (pa_tagstruct_getu32(t, &idx) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!(s = pa_idxset_get_by_index(c->output_streams, idx)) || s->type != PLAYBACK_STREAM) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + s->drain_request = 0; + + pa_memblockq_prebuf_disable(s->memblockq); + + if (!pa_memblockq_is_readable(s->memblockq)) { +/* pa_log("immediate drain: %u\n", pa_memblockq_get_length(s->memblockq)); */ + pa_pstream_send_simple_ack(c->pstream, tag); + } else { +/* pa_log("slow drain triggered\n"); */ + s->drain_request = 1; + s->drain_tag = tag; + + pa_sink_notify(s->sink_input->sink); + } +} + +static void command_stat(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + pa_tagstruct *reply; + assert(c && t); + + if (!pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_putu32(reply, c->protocol->core->memblock_stat->total); + pa_tagstruct_putu32(reply, c->protocol->core->memblock_stat->total_size); + pa_tagstruct_putu32(reply, c->protocol->core->memblock_stat->allocated); + pa_tagstruct_putu32(reply, c->protocol->core->memblock_stat->allocated_size); + pa_tagstruct_putu32(reply, pa_scache_total_size(c->protocol->core)); + pa_pstream_send_tagstruct(c->pstream, reply); +} + +static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + pa_tagstruct *reply; + struct playback_stream *s; + struct timeval tv, now; + uint64_t counter; + uint32_t idx; + assert(c && t); + + if (pa_tagstruct_getu32(t, &idx) < 0 || + pa_tagstruct_get_timeval(t, &tv) < 0 || + pa_tagstruct_getu64(t, &counter) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!(s = pa_idxset_get_by_index(c->output_streams, idx)) || s->type != PLAYBACK_STREAM) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_put_usec(reply, pa_sink_input_get_latency(s->sink_input)); + pa_tagstruct_put_usec(reply, pa_sink_get_latency(s->sink_input->sink)); + pa_tagstruct_put_usec(reply, 0); + pa_tagstruct_put_boolean(reply, pa_memblockq_is_readable(s->memblockq)); + pa_tagstruct_putu32(reply, pa_memblockq_get_length(s->memblockq)); + pa_tagstruct_put_timeval(reply, &tv); + pa_gettimeofday(&now); + pa_tagstruct_put_timeval(reply, &now); + pa_tagstruct_putu64(reply, counter); + pa_pstream_send_tagstruct(c->pstream, reply); +} + +static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + pa_tagstruct *reply; + struct record_stream *s; + struct timeval tv, now; + uint64_t counter; + uint32_t idx; + assert(c && t); + + if (pa_tagstruct_getu32(t, &idx) < 0 || + pa_tagstruct_get_timeval(t, &tv) < 0 || + pa_tagstruct_getu64(t, &counter) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_put_usec(reply, pa_source_output_get_latency(s->source_output)); + pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0); + pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source)); + pa_tagstruct_put_boolean(reply, 0); + pa_tagstruct_putu32(reply, pa_memblockq_get_length(s->memblockq)); + pa_tagstruct_put_timeval(reply, &tv); + pa_gettimeofday(&now); + pa_tagstruct_put_timeval(reply, &now); + pa_tagstruct_putu64(reply, counter); + pa_pstream_send_tagstruct(c->pstream, reply); +} + + +static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + struct upload_stream *s; + size_t length; + const char *name; + pa_sample_spec ss; + pa_channel_map map; + pa_tagstruct *reply; + assert(c && t && c->protocol && c->protocol->core); + + if (pa_tagstruct_gets(t, &name) < 0 || !name || + pa_tagstruct_get_sample_spec(t, &ss) < 0 || + pa_tagstruct_get_channel_map(t, &map) < 0 || + pa_tagstruct_getu32(t, &length) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if ((length % pa_frame_size(&ss)) != 0 || length <= 0 || !*name) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID); + return; + } + + if (!(s = upload_stream_new(c, &ss, &map, name, length))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID); + return; + } + + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_putu32(reply, s->index); + pa_tagstruct_putu32(reply, length); + pa_pstream_send_tagstruct(c->pstream, reply); +} + +static void command_finish_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t channel; + struct upload_stream *s; + uint32_t idx; + assert(c && t); + + if (pa_tagstruct_getu32(t, &channel) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || (s->type != UPLOAD_STREAM)) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); + return; + } + + pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, &idx); + pa_pstream_send_simple_ack(c->pstream, tag); + upload_stream_free(s); +} + +static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t sink_index; + pa_cvolume volume; + pa_sink *sink; + const char *name, *sink_name; + assert(c && t); + + if (pa_tagstruct_getu32(t, &sink_index) < 0 || + pa_tagstruct_gets(t, &sink_name) < 0 || + pa_tagstruct_get_cvolume(t, &volume) < 0 || + pa_tagstruct_gets(t, &name) < 0 || !name || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (sink_index != (uint32_t) -1) + sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index); + else + sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1); + + if (!sink) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + if (pa_scache_play_item(c->protocol->core, name, sink, &volume) < 0) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + const char *name; + assert(c && t); + + if (pa_tagstruct_gets(t, &name) < 0 || !name || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (pa_scache_remove_item(c->protocol->core, name) < 0) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void sink_fill_tagstruct(pa_tagstruct *t, pa_sink *sink) { + assert(t && sink); + pa_tagstruct_put( + t, + PA_TAG_U32, sink->index, + PA_TAG_STRING, sink->name, + PA_TAG_STRING, sink->description, + PA_TAG_SAMPLE_SPEC, &sink->sample_spec, + PA_TAG_CHANNEL_MAP, &sink->channel_map, + PA_TAG_U32, sink->owner ? sink->owner->index : PA_INVALID_INDEX, + PA_TAG_CVOLUME, pa_sink_get_volume(sink, PA_MIXER_HARDWARE), + PA_TAG_U32, sink->monitor_source->index, + PA_TAG_STRING, sink->monitor_source->name, + PA_TAG_USEC, pa_sink_get_latency(sink), + PA_TAG_STRING, sink->driver, + PA_TAG_INVALID); +} + +static void source_fill_tagstruct(pa_tagstruct *t, pa_source *source) { + assert(t && source); + pa_tagstruct_putu32(t, source->index); + pa_tagstruct_puts(t, source->name); + pa_tagstruct_puts(t, source->description); + pa_tagstruct_put_sample_spec(t, &source->sample_spec); + pa_tagstruct_put_channel_map(t, &source->channel_map); + pa_tagstruct_putu32(t, source->owner ? source->owner->index : (uint32_t) -1); + pa_tagstruct_putu32(t, source->monitor_of ? source->monitor_of->index : (uint32_t) -1); + pa_tagstruct_puts(t, source->monitor_of ? source->monitor_of->name : NULL); + pa_tagstruct_put_usec(t, pa_source_get_latency(source)); + pa_tagstruct_puts(t, source->driver); +} + +static void client_fill_tagstruct(pa_tagstruct *t, pa_client *client) { + assert(t && client); + pa_tagstruct_putu32(t, client->index); + pa_tagstruct_puts(t, client->name); + pa_tagstruct_putu32(t, client->owner ? client->owner->index : (uint32_t) -1); + pa_tagstruct_puts(t, client->driver); +} + +static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) { + assert(t && module); + pa_tagstruct_putu32(t, module->index); + pa_tagstruct_puts(t, module->name); + pa_tagstruct_puts(t, module->argument); + pa_tagstruct_putu32(t, module->n_used); + pa_tagstruct_put_boolean(t, module->auto_unload); +} + +static void sink_input_fill_tagstruct(pa_tagstruct *t, pa_sink_input *s) { + assert(t && s); + pa_tagstruct_putu32(t, s->index); + pa_tagstruct_puts(t, s->name); + pa_tagstruct_putu32(t, s->owner ? s->owner->index : (uint32_t) -1); + pa_tagstruct_putu32(t, s->client ? s->client->index : (uint32_t) -1); + pa_tagstruct_putu32(t, s->sink->index); + pa_tagstruct_put_sample_spec(t, &s->sample_spec); + pa_tagstruct_put_channel_map(t, &s->channel_map); + pa_tagstruct_put_cvolume(t, &s->volume); + pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s)); + pa_tagstruct_put_usec(t, pa_sink_get_latency(s->sink)); + pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s))); + pa_tagstruct_puts(t, s->driver); +} + +static void source_output_fill_tagstruct(pa_tagstruct *t, pa_source_output *s) { + assert(t && s); + pa_tagstruct_putu32(t, s->index); + pa_tagstruct_puts(t, s->name); + pa_tagstruct_putu32(t, s->owner ? s->owner->index : (uint32_t) -1); + pa_tagstruct_putu32(t, s->client ? s->client->index : (uint32_t) -1); + pa_tagstruct_putu32(t, s->source->index); + pa_tagstruct_put_sample_spec(t, &s->sample_spec); + pa_tagstruct_put_channel_map(t, &s->channel_map); + pa_tagstruct_put_usec(t, pa_source_output_get_latency(s)); + pa_tagstruct_put_usec(t, pa_source_get_latency(s->source)); + pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s))); + pa_tagstruct_puts(t, s->driver); +} + +static void scache_fill_tagstruct(pa_tagstruct *t, pa_scache_entry *e) { + assert(t && e); + pa_tagstruct_putu32(t, e->index); + pa_tagstruct_puts(t, e->name); + pa_tagstruct_put_cvolume(t, &e->volume); + pa_tagstruct_put_usec(t, pa_bytes_to_usec(e->memchunk.length, &e->sample_spec)); + pa_tagstruct_put_sample_spec(t, &e->sample_spec); + pa_tagstruct_put_channel_map(t, &e->channel_map); + pa_tagstruct_putu32(t, e->memchunk.length); + pa_tagstruct_put_boolean(t, e->lazy); + pa_tagstruct_puts(t, e->filename); +} + +static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t idx; + pa_sink *sink = NULL; + pa_source *source = NULL; + pa_client *client = NULL; + pa_module *module = NULL; + pa_sink_input *si = NULL; + pa_source_output *so = NULL; + pa_scache_entry *sce = NULL; + const char *name; + pa_tagstruct *reply; + assert(c && t); + + + if (pa_tagstruct_getu32(t, &idx) < 0 || + (command != PA_COMMAND_GET_CLIENT_INFO && + command != PA_COMMAND_GET_MODULE_INFO && + command != PA_COMMAND_GET_SINK_INPUT_INFO && + command != PA_COMMAND_GET_SOURCE_OUTPUT_INFO && + pa_tagstruct_gets(t, &name) < 0) || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (command == PA_COMMAND_GET_SINK_INFO) { + if (idx != (uint32_t) -1) + sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); + else + sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1); + } else if (command == PA_COMMAND_GET_SOURCE_INFO) { + if (idx != (uint32_t) -1) + source = pa_idxset_get_by_index(c->protocol->core->sources, idx); + else + source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1); + } else if (command == PA_COMMAND_GET_CLIENT_INFO) + client = pa_idxset_get_by_index(c->protocol->core->clients, idx); + else if (command == PA_COMMAND_GET_MODULE_INFO) + module = pa_idxset_get_by_index(c->protocol->core->modules, idx); + else if (command == PA_COMMAND_GET_SINK_INPUT_INFO) + si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx); + else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO) + so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx); + else { + assert(command == PA_COMMAND_GET_SAMPLE_INFO); + if (idx != (uint32_t) -1) + sce = pa_idxset_get_by_index(c->protocol->core->scache, idx); + else + sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE, 0); + } + + if (!sink && !source && !client && !module && !si && !so && !sce) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + if (sink) + sink_fill_tagstruct(reply, sink); + else if (source) + source_fill_tagstruct(reply, source); + else if (client) + client_fill_tagstruct(reply, client); + else if (module) + module_fill_tagstruct(reply, module); + else if (si) + sink_input_fill_tagstruct(reply, si); + else if (so) + source_output_fill_tagstruct(reply, so); + else + scache_fill_tagstruct(reply, sce); + pa_pstream_send_tagstruct(c->pstream, reply); +} + +static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + pa_idxset *i; + uint32_t idx; + void *p; + pa_tagstruct *reply; + assert(c && t); + + if (!pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + + if (command == PA_COMMAND_GET_SINK_INFO_LIST) + i = c->protocol->core->sinks; + else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST) + i = c->protocol->core->sources; + else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST) + i = c->protocol->core->clients; + else if (command == PA_COMMAND_GET_MODULE_INFO_LIST) + i = c->protocol->core->modules; + else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST) + i = c->protocol->core->sink_inputs; + else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST) + i = c->protocol->core->source_outputs; + else { + assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST); + i = c->protocol->core->scache; + } + + if (i) { + for (p = pa_idxset_first(i, &idx); p; p = pa_idxset_next(i, &idx)) { + if (command == PA_COMMAND_GET_SINK_INFO_LIST) + sink_fill_tagstruct(reply, p); + else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST) + source_fill_tagstruct(reply, p); + else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST) + client_fill_tagstruct(reply, p); + else if (command == PA_COMMAND_GET_MODULE_INFO_LIST) + module_fill_tagstruct(reply, p); + else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST) + sink_input_fill_tagstruct(reply, p); + else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST) + source_output_fill_tagstruct(reply, p); + else { + assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST); + scache_fill_tagstruct(reply, p); + } + } + } + + pa_pstream_send_tagstruct(c->pstream, reply); +} + +static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + pa_tagstruct *reply; + char txt[256]; + const char *n; + assert(c && t); + + if (!pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_puts(reply, PACKAGE_NAME); + pa_tagstruct_puts(reply, PACKAGE_VERSION); + pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt))); + pa_tagstruct_puts(reply, pa_get_fqdn(txt, sizeof(txt))); + pa_tagstruct_put_sample_spec(reply, &c->protocol->core->default_sample_spec); + + n = pa_namereg_get_default_sink_name(c->protocol->core); + pa_tagstruct_puts(reply, n); + n = pa_namereg_get_default_source_name(c->protocol->core); + pa_tagstruct_puts(reply, n); + + pa_tagstruct_putu32(reply, c->protocol->core->cookie); + + pa_pstream_send_tagstruct(c->pstream, reply); +} + +static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint32_t idx, void *userdata) { + pa_tagstruct *t; + struct connection *c = userdata; + assert(c && core); + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE_EVENT); + pa_tagstruct_putu32(t, (uint32_t) -1); + pa_tagstruct_putu32(t, e); + pa_tagstruct_putu32(t, idx); + pa_pstream_send_tagstruct(c->pstream, t); +} + +static void command_subscribe(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + pa_subscription_mask_t m; + assert(c && t); + + if (pa_tagstruct_getu32(t, &m) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (c->subscription) + pa_subscription_free(c->subscription); + + if (m != 0) { + c->subscription = pa_subscription_new(c->protocol->core, m, subscription_cb, c); + assert(c->subscription); + } else + c->subscription = NULL; + + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_set_volume(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t idx; + pa_cvolume volume; + pa_sink *sink = NULL; + pa_sink_input *si = NULL; + const char *name = NULL; + assert(c && t); + + if (pa_tagstruct_getu32(t, &idx) < 0 || + (command == PA_COMMAND_SET_SINK_VOLUME && pa_tagstruct_gets(t, &name) < 0) || + pa_tagstruct_get_cvolume(t, &volume) || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (command == PA_COMMAND_SET_SINK_VOLUME) { + if (idx != (uint32_t) -1) + sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); + else + sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1); + } else { + assert(command == PA_COMMAND_SET_SINK_INPUT_VOLUME); + si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx); + } + + if (!si && !sink) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + if (sink) + pa_sink_set_volume(sink, PA_MIXER_HARDWARE, &volume); + else if (si) + pa_sink_input_set_volume(si, &volume); + + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t idx; + int b; + struct playback_stream *s; + assert(c && t); + + if (pa_tagstruct_getu32(t, &idx) < 0 || + pa_tagstruct_get_boolean(t, &b) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!(s = pa_idxset_get_by_index(c->output_streams, idx)) || s->type != PLAYBACK_STREAM) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + pa_sink_input_cork(s->sink_input, b); + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_flush_or_trigger_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t idx; + struct playback_stream *s; + assert(c && t); + + if (pa_tagstruct_getu32(t, &idx) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!(s = pa_idxset_get_by_index(c->output_streams, idx)) || s->type != PLAYBACK_STREAM) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + if (command == PA_COMMAND_PREBUF_PLAYBACK_STREAM) + pa_memblockq_prebuf_reenable(s->memblockq); + else if (command == PA_COMMAND_TRIGGER_PLAYBACK_STREAM) + pa_memblockq_prebuf_disable(s->memblockq); + else { + assert(command == PA_COMMAND_FLUSH_PLAYBACK_STREAM); + pa_memblockq_flush(s->memblockq); + /*pa_log(__FILE__": flush: %u\n", pa_memblockq_get_length(s->memblockq));*/ + } + + pa_sink_notify(s->sink_input->sink); + pa_pstream_send_simple_ack(c->pstream, tag); + request_bytes(s); +} + +static void command_cork_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t idx; + struct record_stream *s; + int b; + assert(c && t); + + if (pa_tagstruct_getu32(t, &idx) < 0 || + pa_tagstruct_get_boolean(t, &b) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + pa_source_output_cork(s->source_output, b); + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_flush_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t idx; + struct record_stream *s; + assert(c && t); + + if (pa_tagstruct_getu32(t, &idx) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + pa_memblockq_flush(s->memblockq); + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t idx; + const char *s; + assert(c && t); + + if (pa_tagstruct_getu32(t, &idx) < 0 || + pa_tagstruct_gets(t, &s) < 0 || !s || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + pa_namereg_set_default(c->protocol->core, s, command == PA_COMMAND_SET_DEFAULT_SOURCE ? PA_NAMEREG_SOURCE : PA_NAMEREG_SINK); + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t idx; + const char *name; + assert(c && t); + + if (pa_tagstruct_getu32(t, &idx) < 0 || + pa_tagstruct_gets(t, &name) < 0 || !name || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (command == PA_COMMAND_SET_PLAYBACK_STREAM_NAME) { + struct playback_stream *s; + + if (!(s = pa_idxset_get_by_index(c->output_streams, idx)) || s->type != PLAYBACK_STREAM) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + pa_sink_input_set_name(s->sink_input, name); + + } else { + struct record_stream *s; + + if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + pa_source_output_set_name(s->source_output, name); + } + + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t idx; + assert(c && t); + + if (pa_tagstruct_getu32(t, &idx) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (command == PA_COMMAND_KILL_CLIENT) { + pa_client *client; + + if (!(client = pa_idxset_get_by_index(c->protocol->core->clients, idx))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + pa_client_kill(client); + } else if (command == PA_COMMAND_KILL_SINK_INPUT) { + pa_sink_input *s; + + if (!(s = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + pa_sink_input_kill(s); + } else { + pa_source_output *s; + + assert(command == PA_COMMAND_KILL_SOURCE_OUTPUT); + + if (!(s = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + pa_source_output_kill(s); + } + + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_load_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + pa_module *m; + const char *name, *argument; + pa_tagstruct *reply; + assert(c && t); + + if (pa_tagstruct_gets(t, &name) < 0 || !name || + pa_tagstruct_gets(t, &argument) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!(m = pa_module_load(c->protocol->core, name, argument))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_INITFAILED); + return; + } + + reply = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_putu32(reply, m->index); + pa_pstream_send_tagstruct(c->pstream, reply); +} + +static void command_unload_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t idx; + pa_module *m; + assert(c && t); + + if (pa_tagstruct_getu32(t, &idx) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!(m = pa_idxset_get_by_index(c->protocol->core->modules, idx))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + pa_module_unload_request(m); + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_add_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + const char *name, *module, *argument; + uint32_t type; + uint32_t idx; + pa_tagstruct *reply; + assert(c && t); + + if (pa_tagstruct_gets(t, &name) < 0 || !name || + pa_tagstruct_getu32(t, &type) < 0 || type > 1 || + pa_tagstruct_gets(t, &module) < 0 || !module || + pa_tagstruct_gets(t, &argument) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (pa_autoload_add(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, module, argument, &idx) < 0) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); + return; + } + + reply = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_putu32(reply, idx); + pa_pstream_send_tagstruct(c->pstream, reply); +} + +static void command_remove_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + const char *name = NULL; + uint32_t type, idx = PA_IDXSET_INVALID; + int r; + assert(c && t); + + if ((pa_tagstruct_getu32(t, &idx) < 0 && + (pa_tagstruct_gets(t, &name) < 0 || + pa_tagstruct_getu32(t, &type) < 0)) || + (!name && idx == PA_IDXSET_INVALID) || + (name && type > 1) || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (name) + r = pa_autoload_remove_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE); + else + r = pa_autoload_remove_by_index(c->protocol->core, idx); + + if (r < 0) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void autoload_fill_tagstruct(pa_tagstruct *t, const pa_autoload_entry *e) { + assert(t && e); + + pa_tagstruct_putu32(t, e->index); + pa_tagstruct_puts(t, e->name); + pa_tagstruct_putu32(t, e->type == PA_NAMEREG_SINK ? 0 : 1); + pa_tagstruct_puts(t, e->module); + pa_tagstruct_puts(t, e->argument); +} + +static void command_get_autoload_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + const pa_autoload_entry *a = NULL; + uint32_t type, idx; + const char *name; + pa_tagstruct *reply; + assert(c && t); + + if ((pa_tagstruct_getu32(t, &idx) < 0 && + (pa_tagstruct_gets(t, &name) < 0 || + pa_tagstruct_getu32(t, &type) < 0)) || + (!name && idx == PA_IDXSET_INVALID) || + (name && type > 1) || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + + if (name) + a = pa_autoload_get_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE); + else + a = pa_autoload_get_by_index(c->protocol->core, idx); + + if (!a) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + autoload_fill_tagstruct(reply, a); + pa_pstream_send_tagstruct(c->pstream, reply); +} + +static void command_get_autoload_info_list(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + pa_tagstruct *reply; + assert(c && t); + + if (!pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + reply = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + + if (c->protocol->core->autoload_hashmap) { + pa_autoload_entry *a; + void *state = NULL; + + while ((a = pa_hashmap_iterate(c->protocol->core->autoload_hashmap, &state, NULL))) + autoload_fill_tagstruct(reply, a); + } + + pa_pstream_send_tagstruct(c->pstream, reply); +} + +/*** pstream callbacks ***/ + +static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, void *userdata) { + struct connection *c = userdata; + assert(p && packet && packet->data && c); + + if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) { + pa_log(__FILE__": invalid packet.\n"); + connection_free(c); + } +} + +static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata) { + struct connection *c = userdata; + struct output_stream *stream; + assert(p && chunk && userdata); + + if (!(stream = pa_idxset_get_by_index(c->output_streams, channel))) { + pa_log(__FILE__": client sent block for invalid stream.\n"); + connection_free(c); + return; + } + + if (stream->type == PLAYBACK_STREAM) { + struct playback_stream *ps = (struct playback_stream*) stream; + if (chunk->length >= ps->requested_bytes) + ps->requested_bytes = 0; + else + ps->requested_bytes -= chunk->length; + + pa_memblockq_push_align(ps->memblockq, chunk, delta); + assert(ps->sink_input); +/* pa_log(__FILE__": after_recv: %u\n", pa_memblockq_get_length(p->memblockq)); */ + + pa_sink_notify(ps->sink_input->sink); +/* pa_log(__FILE__": Recieved %u bytes.\n", chunk->length); */ + + } else { + struct upload_stream *u = (struct upload_stream*) stream; + size_t l; + assert(u->type == UPLOAD_STREAM); + + if (!u->memchunk.memblock) { + if (u->length == chunk->length) { + u->memchunk = *chunk; + pa_memblock_ref(u->memchunk.memblock); + u->length = 0; + } else { + u->memchunk.memblock = pa_memblock_new(u->length, c->protocol->core->memblock_stat); + u->memchunk.index = u->memchunk.length = 0; + } + } + + assert(u->memchunk.memblock); + + l = u->length; + if (l > chunk->length) + l = chunk->length; + + if (l > 0) { + memcpy((uint8_t*) u->memchunk.memblock->data + u->memchunk.index + u->memchunk.length, + (uint8_t*) chunk->memblock->data+chunk->index, l); + u->memchunk.length += l; + u->length -= l; + } + } +} + +static void pstream_die_callback(pa_pstream *p, void *userdata) { + struct connection *c = userdata; + assert(p && c); + connection_free(c); + +/* pa_log(__FILE__": connection died.\n");*/ +} + + +static void pstream_drain_callback(pa_pstream *p, void *userdata) { + struct connection *c = userdata; + assert(p && c); + + send_memblock(c); +} + +/*** client callbacks ***/ + +static void client_kill_cb(pa_client *c) { + assert(c && c->userdata); + connection_free(c->userdata); +} + +/*** socket server callbacks ***/ + +static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) { + struct connection *c = userdata; + assert(m && tv && c && c->auth_timeout_event == e); + + if (!c->authorized) + connection_free(c); +} + +static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, void *userdata) { + pa_protocol_native *p = userdata; + struct connection *c; + assert(io && p); + + if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) { + pa_log_warn(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS); + pa_iochannel_free(io); + return; + } + + c = pa_xmalloc(sizeof(struct connection)); + + c->authorized =!! p->public; + + if (!c->authorized) { + struct timeval tv; + pa_gettimeofday(&tv); + tv.tv_sec += AUTH_TIMEOUT; + c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c); + } else + c->auth_timeout_event = NULL; + + c->protocol = p; + assert(p->core); + c->client = pa_client_new(p->core, __FILE__, "Client"); + assert(c->client); + c->client->kill = client_kill_cb; + c->client->userdata = c; + c->client->owner = p->module; + + c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->memblock_stat); + assert(c->pstream); + + pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c); + pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c); + pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c); + pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c); + + c->pdispatch = pa_pdispatch_new(p->core->mainloop, command_table, PA_COMMAND_MAX); + assert(c->pdispatch); + + c->record_streams = pa_idxset_new(NULL, NULL); + c->output_streams = pa_idxset_new(NULL, NULL); + assert(c->record_streams && c->output_streams); + + c->rrobin_index = PA_IDXSET_INVALID; + c->subscription = NULL; + + pa_idxset_put(p->connections, c, NULL); +} + +/*** module entry points ***/ + +static int load_key(pa_protocol_native*p, const char*fn) { + assert(p); + + p->auth_cookie_in_property = 0; + + if (!fn && pa_authkey_prop_get(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0) { + pa_log_info(__FILE__": using already loaded auth cookie.\n"); + pa_authkey_prop_ref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME); + p->auth_cookie_in_property = 1; + return 0; + } + + if (!fn) + fn = PA_NATIVE_COOKIE_FILE; + + if (pa_authkey_load_auto(fn, p->auth_cookie, sizeof(p->auth_cookie)) < 0) + return -1; + + pa_log_info(__FILE__": loading cookie from disk.\n"); + + if (pa_authkey_prop_put(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0) + p->auth_cookie_in_property = 1; + + return 0; +} + +static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_modargs *ma) { + pa_protocol_native *p; + int public = 0; + assert(c && ma); + + if (pa_modargs_get_value_boolean(ma, "public", &public) < 0) { + pa_log(__FILE__": public= expects a boolean argument.\n"); + return NULL; + } + + p = pa_xmalloc(sizeof(pa_protocol_native)); + p->core = c; + p->module = m; + p->public = public; + p->server = NULL; + + if (load_key(p, pa_modargs_get_value(ma, "cookie", NULL)) < 0) { + pa_xfree(p); + return NULL; + } + + p->connections = pa_idxset_new(NULL, NULL); + assert(p->connections); + + return p; +} + +pa_protocol_native* pa_protocol_native_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma) { + char t[256]; + pa_protocol_native *p; + + if (!(p = protocol_new_internal(core, m, ma))) + return NULL; + + p->server = server; + pa_socket_server_set_callback(p->server, on_connection, p); + + if (pa_socket_server_get_address(p->server, t, sizeof(t))) { + pa_strlist *l; + l = pa_property_get(core, PA_NATIVE_SERVER_PROPERTY_NAME); + l = pa_strlist_prepend(l, t); + pa_property_replace(core, PA_NATIVE_SERVER_PROPERTY_NAME, l); + } + + return p; +} + +void pa_protocol_native_free(pa_protocol_native *p) { + struct connection *c; + assert(p); + + while ((c = pa_idxset_first(p->connections, NULL))) + connection_free(c); + pa_idxset_free(p->connections, NULL, NULL); + + if (p->server) { + char t[256]; + + if (pa_socket_server_get_address(p->server, t, sizeof(t))) { + pa_strlist *l; + l = pa_property_get(p->core, PA_NATIVE_SERVER_PROPERTY_NAME); + l = pa_strlist_remove(l, t); + + if (l) + pa_property_replace(p->core, PA_NATIVE_SERVER_PROPERTY_NAME, l); + else + pa_property_remove(p->core, PA_NATIVE_SERVER_PROPERTY_NAME); + } + + pa_socket_server_unref(p->server); + } + + if (p->auth_cookie_in_property) + pa_authkey_prop_unref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME); + + pa_xfree(p); +} + +pa_protocol_native* pa_protocol_native_new_iochannel(pa_core*core, pa_iochannel *io, pa_module *m, pa_modargs *ma) { + pa_protocol_native *p; + + if (!(p = protocol_new_internal(core, m, ma))) + return NULL; + + on_connection(NULL, io, p); + + return p; +} diff --git a/src/polypcore/protocol-native.h b/src/polypcore/protocol-native.h new file mode 100644 index 00000000..12e85d0b --- /dev/null +++ b/src/polypcore/protocol-native.h @@ -0,0 +1,37 @@ +#ifndef fooprotocolnativehfoo +#define fooprotocolnativehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "core.h" +#include "socket-server.h" +#include "module.h" +#include "modargs.h" + +typedef struct pa_protocol_native pa_protocol_native; + +pa_protocol_native* pa_protocol_native_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma); +void pa_protocol_native_free(pa_protocol_native *n); + +pa_protocol_native* pa_protocol_native_new_iochannel(pa_core*core, pa_iochannel *io, pa_module *m, pa_modargs *ma); + +#endif diff --git a/src/polypcore/protocol-simple.c b/src/polypcore/protocol-simple.c new file mode 100644 index 00000000..113919b3 --- /dev/null +++ b/src/polypcore/protocol-simple.c @@ -0,0 +1,453 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "sink-input.h" +#include "source-output.h" +#include "protocol-simple.h" +#include "client.h" +#include "sample-util.h" +#include "namereg.h" +#include "xmalloc.h" +#include "log.h" + +/* Don't allow more than this many concurrent connections */ +#define MAX_CONNECTIONS 10 + +struct connection { + pa_protocol_simple *protocol; + pa_iochannel *io; + pa_sink_input *sink_input; + pa_source_output *source_output; + pa_client *client; + pa_memblockq *input_memblockq, *output_memblockq; + pa_defer_event *defer_event; + + struct { + pa_memblock *current_memblock; + size_t memblock_index, fragment_size; + } playback; +}; + +struct pa_protocol_simple { + pa_module *module; + pa_core *core; + pa_socket_server*server; + pa_idxset *connections; + enum { + RECORD = 1, + PLAYBACK = 2, + DUPLEX = 3 + } mode; + pa_sample_spec sample_spec; + char *source_name, *sink_name; +}; + +#define PLAYBACK_BUFFER_SECONDS (.5) +#define PLAYBACK_BUFFER_FRAGMENTS (10) +#define RECORD_BUFFER_SECONDS (5) +#define RECORD_BUFFER_FRAGMENTS (100) + +static void connection_free(struct connection *c) { + assert(c); + + pa_idxset_remove_by_data(c->protocol->connections, c, NULL); + + if (c->playback.current_memblock) + pa_memblock_unref(c->playback.current_memblock); + if (c->sink_input) { + pa_sink_input_disconnect(c->sink_input); + pa_sink_input_unref(c->sink_input); + } + if (c->source_output) { + pa_source_output_disconnect(c->source_output); + pa_source_output_unref(c->source_output); + } + if (c->client) + pa_client_free(c->client); + if (c->io) + pa_iochannel_free(c->io); + if (c->input_memblockq) + pa_memblockq_free(c->input_memblockq); + if (c->output_memblockq) + pa_memblockq_free(c->output_memblockq); + if (c->defer_event) + c->protocol->core->mainloop->defer_free(c->defer_event); + pa_xfree(c); +} + +static int do_read(struct connection *c) { + pa_memchunk chunk; + ssize_t r; + size_t l; + + if (!c->sink_input || !(l = pa_memblockq_missing(c->input_memblockq))) + return 0; + + if (l > c->playback.fragment_size) + l = c->playback.fragment_size; + + if (c->playback.current_memblock) + if (c->playback.current_memblock->length - c->playback.memblock_index < l) { + pa_memblock_unref(c->playback.current_memblock); + c->playback.current_memblock = NULL; + c->playback.memblock_index = 0; + } + + if (!c->playback.current_memblock) { + c->playback.current_memblock = pa_memblock_new(c->playback.fragment_size*2, c->protocol->core->memblock_stat); + assert(c->playback.current_memblock && c->playback.current_memblock->length >= l); + c->playback.memblock_index = 0; + } + + if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) { + pa_log(__FILE__": read() failed: %s\n", r == 0 ? "EOF" : strerror(errno)); + return -1; + } + + chunk.memblock = c->playback.current_memblock; + chunk.index = c->playback.memblock_index; + chunk.length = r; + assert(chunk.memblock); + + c->playback.memblock_index += r; + + assert(c->input_memblockq); + pa_memblockq_push_align(c->input_memblockq, &chunk, 0); + assert(c->sink_input); + pa_sink_notify(c->sink_input->sink); + + return 0; +} + +static int do_write(struct connection *c) { + pa_memchunk chunk; + ssize_t r; + + if (!c->source_output) + return 0; + + assert(c->output_memblockq); + if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) + return 0; + + assert(chunk.memblock && chunk.length); + + if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) { + pa_memblock_unref(chunk.memblock); + pa_log(__FILE__": write(): %s\n", strerror(errno)); + return -1; + } + + pa_memblockq_drop(c->output_memblockq, &chunk, r); + pa_memblock_unref(chunk.memblock); + + return 0; +} + + +static void do_work(struct connection *c) { + assert(c); + + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); + c->protocol->core->mainloop->defer_enable(c->defer_event, 0); + + if (pa_iochannel_is_writable(c->io)) + if (do_write(c) < 0) + goto fail; + + if (pa_iochannel_is_readable(c->io)) + if (do_read(c) < 0) + goto fail; + + if (pa_iochannel_is_hungup(c->io)) + c->protocol->core->mainloop->defer_enable(c->defer_event, 1); + + return; + +fail: + connection_free(c); +} + +/*** sink_input callbacks ***/ + +static int sink_input_peek_cb(pa_sink_input *i, pa_memchunk *chunk) { + struct connection*c; + assert(i && i->userdata && chunk); + c = i->userdata; + + if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) + return -1; + + return 0; +} + +static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { + struct connection*c = i->userdata; + assert(i && c && length); + + pa_memblockq_drop(c->input_memblockq, chunk, length); + + /* do something */ + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); + c->protocol->core->mainloop->defer_enable(c->defer_event, 1); +} + +static void sink_input_kill_cb(pa_sink_input *i) { + assert(i && i->userdata); + connection_free((struct connection *) i->userdata); +} + + +static pa_usec_t sink_input_get_latency_cb(pa_sink_input *i) { + struct connection*c = i->userdata; + assert(i && c); + return pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); +} + +/*** source_output callbacks ***/ + +static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { + struct connection *c = o->userdata; + assert(o && c && chunk); + + pa_memblockq_push(c->output_memblockq, chunk, 0); + + /* do something */ + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable); + c->protocol->core->mainloop->defer_enable(c->defer_event, 1); +} + +static void source_output_kill_cb(pa_source_output *o) { + assert(o && o->userdata); + connection_free((struct connection *) o->userdata); +} + +static pa_usec_t source_output_get_latency_cb(pa_source_output *o) { + struct connection*c = o->userdata; + assert(o && c); + return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec); +} + +/*** client callbacks ***/ + +static void client_kill_cb(pa_client *c) { + assert(c && c->userdata); + connection_free((struct connection *) c->userdata); +} + +/*** pa_iochannel callbacks ***/ + +static void io_callback(pa_iochannel*io, void *userdata) { + struct connection *c = userdata; + assert(io && c && c->io == io); + + do_work(c); +} + +/*** fixed callback ***/ + +static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) { + struct connection *c = userdata; + assert(a && c && c->defer_event == e); + + do_work(c); +} + +/*** socket_server callbacks ***/ + +static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) { + pa_protocol_simple *p = userdata; + struct connection *c = NULL; + char cname[256]; + assert(s && io && p); + + if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) { + pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS); + pa_iochannel_free(io); + return; + } + + c = pa_xmalloc(sizeof(struct connection)); + c->io = io; + c->sink_input = NULL; + c->source_output = NULL; + c->defer_event = NULL; + c->input_memblockq = c->output_memblockq = NULL; + c->protocol = p; + c->playback.current_memblock = NULL; + c->playback.memblock_index = 0; + c->playback.fragment_size = 0; + + pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); + c->client = pa_client_new(p->core, __FILE__, cname); + assert(c->client); + c->client->owner = p->module; + c->client->kill = client_kill_cb; + c->client->userdata = c; + + if (p->mode & PLAYBACK) { + pa_sink *sink; + size_t l; + + if (!(sink = pa_namereg_get(p->core, p->sink_name, PA_NAMEREG_SINK, 1))) { + pa_log(__FILE__": Failed to get sink.\n"); + goto fail; + } + + if (!(c->sink_input = pa_sink_input_new(sink, __FILE__, c->client->name, &p->sample_spec, NULL, 0, -1))) { + pa_log(__FILE__": Failed to create sink input.\n"); + goto fail; + } + + c->sink_input->owner = p->module; + c->sink_input->client = c->client; + + c->sink_input->peek = sink_input_peek_cb; + c->sink_input->drop = sink_input_drop_cb; + c->sink_input->kill = sink_input_kill_cb; + c->sink_input->get_latency = sink_input_get_latency_cb; + c->sink_input->userdata = c; + + l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS); + c->input_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&p->sample_spec), l/2, l/PLAYBACK_BUFFER_FRAGMENTS, p->core->memblock_stat); + assert(c->input_memblockq); + pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5); + c->playback.fragment_size = l/10; + } + + if (p->mode & RECORD) { + pa_source *source; + size_t l; + + if (!(source = pa_namereg_get(p->core, p->source_name, PA_NAMEREG_SOURCE, 1))) { + pa_log(__FILE__": Failed to get source.\n"); + goto fail; + } + + c->source_output = pa_source_output_new(source, __FILE__, c->client->name, &p->sample_spec, NULL, -1); + if (!c->source_output) { + pa_log(__FILE__": Failed to create source output.\n"); + goto fail; + } + c->source_output->owner = p->module; + c->source_output->client = c->client; + + c->source_output->push = source_output_push_cb; + c->source_output->kill = source_output_kill_cb; + c->source_output->get_latency = source_output_get_latency_cb; + c->source_output->userdata = c; + + l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS); + c->output_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&p->sample_spec), 0, 0, p->core->memblock_stat); + pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2); + } + + pa_iochannel_set_callback(c->io, io_callback, c); + pa_idxset_put(p->connections, c, NULL); + + c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c); + assert(c->defer_event); + p->core->mainloop->defer_enable(c->defer_event, 0); + + return; + +fail: + if (c) + connection_free(c); +} + +pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma) { + pa_protocol_simple* p = NULL; + int enable; + assert(core && server && ma); + + p = pa_xmalloc0(sizeof(pa_protocol_simple)); + p->module = m; + p->core = core; + p->server = server; + p->connections = pa_idxset_new(NULL, NULL); + + p->sample_spec = core->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &p->sample_spec) < 0) { + pa_log(__FILE__": Failed to parse sample type specification.\n"); + goto fail; + } + + p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL)); + p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); + + enable = 0; + if (pa_modargs_get_value_boolean(ma, "record", &enable) < 0) { + pa_log(__FILE__": record= expects a numeric argument.\n"); + goto fail; + } + p->mode = enable ? RECORD : 0; + + enable = 1; + if (pa_modargs_get_value_boolean(ma, "playback", &enable) < 0) { + pa_log(__FILE__": playback= expects a numeric argument.\n"); + goto fail; + } + p->mode |= enable ? PLAYBACK : 0; + + if ((p->mode & (RECORD|PLAYBACK)) == 0) { + pa_log(__FILE__": neither playback nor recording enabled for protocol.\n"); + goto fail; + } + + pa_socket_server_set_callback(p->server, on_connection, p); + + return p; + +fail: + if (p) + pa_protocol_simple_free(p); + return NULL; +} + + +void pa_protocol_simple_free(pa_protocol_simple *p) { + struct connection *c; + assert(p); + + if (p->connections) { + while((c = pa_idxset_first(p->connections, NULL))) + connection_free(c); + + pa_idxset_free(p->connections, NULL, NULL); + } + + if (p->server) + pa_socket_server_unref(p->server); + pa_xfree(p); +} + diff --git a/src/polypcore/protocol-simple.h b/src/polypcore/protocol-simple.h new file mode 100644 index 00000000..63455a53 --- /dev/null +++ b/src/polypcore/protocol-simple.h @@ -0,0 +1,35 @@ +#ifndef fooprotocolsimplehfoo +#define fooprotocolsimplehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "socket-server.h" +#include "module.h" +#include "core.h" +#include "modargs.h" + +typedef struct pa_protocol_simple pa_protocol_simple; + +pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *server, pa_module *m, pa_modargs *ma); +void pa_protocol_simple_free(pa_protocol_simple *n); + +#endif diff --git a/src/polypcore/pstream-util.c b/src/polypcore/pstream-util.c new file mode 100644 index 00000000..ecd63d15 --- /dev/null +++ b/src/polypcore/pstream-util.c @@ -0,0 +1,61 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "native-common.h" +#include "pstream-util.h" + +void pa_pstream_send_tagstruct(pa_pstream *p, pa_tagstruct *t) { + size_t length; + uint8_t *data; + pa_packet *packet; + assert(p); + assert(t); + + data = pa_tagstruct_free_data(t, &length); + assert(data && length); + packet = pa_packet_new_dynamic(data, length); + assert(packet); + pa_pstream_send_packet(p, packet); + pa_packet_unref(packet); +} + +void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error) { + pa_tagstruct *t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_ERROR); + pa_tagstruct_putu32(t, tag); + pa_tagstruct_putu32(t, error); + pa_pstream_send_tagstruct(p, t); +} + +void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag) { + pa_tagstruct *t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_REPLY); + pa_tagstruct_putu32(t, tag); + pa_pstream_send_tagstruct(p, t); +} diff --git a/src/polypcore/pstream-util.h b/src/polypcore/pstream-util.h new file mode 100644 index 00000000..601a9e99 --- /dev/null +++ b/src/polypcore/pstream-util.h @@ -0,0 +1,35 @@ +#ifndef foopstreamutilhfoo +#define foopstreamutilhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include "pstream.h" +#include "tagstruct.h" + +/* The tagstruct is freed!*/ +void pa_pstream_send_tagstruct(pa_pstream *p, pa_tagstruct *t); + +void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error); +void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag); + +#endif diff --git a/src/polypcore/pstream.c b/src/polypcore/pstream.c new file mode 100644 index 00000000..bd00ba4f --- /dev/null +++ b/src/polypcore/pstream.c @@ -0,0 +1,492 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#include "winsock.h" + +#include "pstream.h" +#include "queue.h" +#include "xmalloc.h" +#include "log.h" + +typedef enum pa_pstream_descriptor_index { + PA_PSTREAM_DESCRIPTOR_LENGTH, + PA_PSTREAM_DESCRIPTOR_CHANNEL, + PA_PSTREAM_DESCRIPTOR_DELTA, + PA_PSTREAM_DESCRIPTOR_MAX +} pa_pstream_descriptor_index; + +typedef uint32_t pa_pstream_descriptor[PA_PSTREAM_DESCRIPTOR_MAX]; + +#define PA_PSTREAM_DESCRIPTOR_SIZE (PA_PSTREAM_DESCRIPTOR_MAX*sizeof(uint32_t)) +#define FRAME_SIZE_MAX (1024*500) /* half a megabyte */ + +struct item_info { + enum { PA_PSTREAM_ITEM_PACKET, PA_PSTREAM_ITEM_MEMBLOCK } type; + + /* memblock info */ + pa_memchunk chunk; + uint32_t channel; + uint32_t delta; + + /* packet info */ + pa_packet *packet; +}; + +struct pa_pstream { + int ref; + + pa_mainloop_api *mainloop; + pa_defer_event *defer_event; + pa_iochannel *io; + pa_queue *send_queue; + + int dead; + void (*die_callback) (pa_pstream *p, void *userdata); + void *die_callback_userdata; + + struct { + struct item_info* current; + pa_pstream_descriptor descriptor; + void *data; + size_t index; + } write; + + struct { + pa_memblock *memblock; + pa_packet *packet; + pa_pstream_descriptor descriptor; + void *data; + size_t index; + } read; + + void (*recieve_packet_callback) (pa_pstream *p, pa_packet *packet, void *userdata); + void *recieve_packet_callback_userdata; + + void (*recieve_memblock_callback) (pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata); + void *recieve_memblock_callback_userdata; + + void (*drain_callback)(pa_pstream *p, void *userdata); + void *drain_userdata; + + pa_memblock_stat *memblock_stat; +}; + +static void do_write(pa_pstream *p); +static void do_read(pa_pstream *p); + +static void do_something(pa_pstream *p) { + assert(p); + + p->mainloop->defer_enable(p->defer_event, 0); + + pa_pstream_ref(p); + + if (!p->dead && pa_iochannel_is_readable(p->io)) + do_read(p); + + if (!p->dead && pa_iochannel_is_writable(p->io)) + do_write(p); + + /* In case the line was hungup, make sure to rerun this function + as soon as possible, until all data has been read. */ + + if (!p->dead && pa_iochannel_is_hungup(p->io)) + p->mainloop->defer_enable(p->defer_event, 1); + + pa_pstream_unref(p); +} + +static void io_callback(pa_iochannel*io, void *userdata) { + pa_pstream *p = userdata; + assert(p && p->io == io); + do_something(p); +} + +static void defer_callback(pa_mainloop_api *m, pa_defer_event *e, void*userdata) { + pa_pstream *p = userdata; + assert(p && p->defer_event == e && p->mainloop == m); + do_something(p); +} + +pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_memblock_stat *s) { + pa_pstream *p; + assert(io); + + p = pa_xmalloc(sizeof(pa_pstream)); + p->ref = 1; + p->io = io; + pa_iochannel_set_callback(io, io_callback, p); + + p->dead = 0; + p->die_callback = NULL; + p->die_callback_userdata = NULL; + + p->mainloop = m; + p->defer_event = m->defer_new(m, defer_callback, p); + m->defer_enable(p->defer_event, 0); + + p->send_queue = pa_queue_new(); + assert(p->send_queue); + + p->write.current = NULL; + p->write.index = 0; + + p->read.memblock = NULL; + p->read.packet = NULL; + p->read.index = 0; + + p->recieve_packet_callback = NULL; + p->recieve_packet_callback_userdata = NULL; + + p->recieve_memblock_callback = NULL; + p->recieve_memblock_callback_userdata = NULL; + + p->drain_callback = NULL; + p->drain_userdata = NULL; + + p->memblock_stat = s; + + pa_iochannel_socket_set_rcvbuf(io, 1024*8); + pa_iochannel_socket_set_sndbuf(io, 1024*8); + + return p; +} + +static void item_free(void *item, PA_GCC_UNUSED void *p) { + struct item_info *i = item; + assert(i); + + if (i->type == PA_PSTREAM_ITEM_MEMBLOCK) { + assert(i->chunk.memblock); + pa_memblock_unref(i->chunk.memblock); + } else { + assert(i->type == PA_PSTREAM_ITEM_PACKET); + assert(i->packet); + pa_packet_unref(i->packet); + } + + pa_xfree(i); +} + +static void pstream_free(pa_pstream *p) { + assert(p); + + pa_pstream_close(p); + + pa_queue_free(p->send_queue, item_free, NULL); + + if (p->write.current) + item_free(p->write.current, NULL); + + if (p->read.memblock) + pa_memblock_unref(p->read.memblock); + + if (p->read.packet) + pa_packet_unref(p->read.packet); + + pa_xfree(p); +} + +void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet) { + struct item_info *i; + assert(p && packet && p->ref >= 1); + + if (p->dead) + return; + +/* pa_log(__FILE__": push-packet %p\n", packet); */ + + i = pa_xmalloc(sizeof(struct item_info)); + i->type = PA_PSTREAM_ITEM_PACKET; + i->packet = pa_packet_ref(packet); + + pa_queue_push(p->send_queue, i); + p->mainloop->defer_enable(p->defer_event, 1); +} + +void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk) { + struct item_info *i; + assert(p && channel != (uint32_t) -1 && chunk && p->ref >= 1); + + if (p->dead) + return; + +/* pa_log(__FILE__": push-memblock %p\n", chunk); */ + + i = pa_xmalloc(sizeof(struct item_info)); + i->type = PA_PSTREAM_ITEM_MEMBLOCK; + i->chunk = *chunk; + i->channel = channel; + i->delta = delta; + + pa_memblock_ref(i->chunk.memblock); + + pa_queue_push(p->send_queue, i); + p->mainloop->defer_enable(p->defer_event, 1); +} + +void pa_pstream_set_recieve_packet_callback(pa_pstream *p, void (*callback) (pa_pstream *p, pa_packet *packet, void *userdata), void *userdata) { + assert(p && callback); + + p->recieve_packet_callback = callback; + p->recieve_packet_callback_userdata = userdata; +} + +void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, void (*callback) (pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata), void *userdata) { + assert(p && callback); + + p->recieve_memblock_callback = callback; + p->recieve_memblock_callback_userdata = userdata; +} + +static void prepare_next_write_item(pa_pstream *p) { + assert(p); + + if (!(p->write.current = pa_queue_pop(p->send_queue))) + return; + + p->write.index = 0; + + if (p->write.current->type == PA_PSTREAM_ITEM_PACKET) { + /*pa_log(__FILE__": pop-packet %p\n", p->write.current->packet);*/ + + assert(p->write.current->packet); + p->write.data = p->write.current->packet->data; + p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->packet->length); + p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1); + p->write.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA] = 0; + } else { + assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK && p->write.current->chunk.memblock); + p->write.data = (uint8_t*) p->write.current->chunk.memblock->data + p->write.current->chunk.index; + p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->chunk.length); + p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl(p->write.current->channel); + p->write.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA] = htonl(p->write.current->delta); + } +} + +static void do_write(pa_pstream *p) { + void *d; + size_t l; + ssize_t r; + assert(p); + + if (!p->write.current) + prepare_next_write_item(p); + + if (!p->write.current) + return; + + assert(p->write.data); + + if (p->write.index < PA_PSTREAM_DESCRIPTOR_SIZE) { + d = (uint8_t*) p->write.descriptor + p->write.index; + l = PA_PSTREAM_DESCRIPTOR_SIZE - p->write.index; + } else { + d = (uint8_t*) p->write.data + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE; + l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE); + } + + if ((r = pa_iochannel_write(p->io, d, l)) < 0) + goto die; + + p->write.index += r; + + if (p->write.index >= PA_PSTREAM_DESCRIPTOR_SIZE+ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])) { + assert(p->write.current); + item_free(p->write.current, (void *) 1); + p->write.current = NULL; + + if (p->drain_callback && !pa_pstream_is_pending(p)) + p->drain_callback(p, p->drain_userdata); + } + + return; + +die: + p->dead = 1; + if (p->die_callback) + p->die_callback(p, p->die_callback_userdata); +} + +static void do_read(pa_pstream *p) { + void *d; + size_t l; + ssize_t r; + assert(p); + + if (p->read.index < PA_PSTREAM_DESCRIPTOR_SIZE) { + d = (uint8_t*) p->read.descriptor + p->read.index; + l = PA_PSTREAM_DESCRIPTOR_SIZE - p->read.index; + } else { + assert(p->read.data); + d = (uint8_t*) p->read.data + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE; + l = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE); + } + + if ((r = pa_iochannel_read(p->io, d, l)) <= 0) + goto die; + + p->read.index += r; + + if (p->read.index == PA_PSTREAM_DESCRIPTOR_SIZE) { + /* Reading of frame descriptor complete */ + + /* Frame size too large */ + if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) > FRAME_SIZE_MAX) { + pa_log(__FILE__": Frame size too large\n"); + goto die; + } + + assert(!p->read.packet && !p->read.memblock); + + if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]) == (uint32_t) -1) { + /* Frame is a packet frame */ + p->read.packet = pa_packet_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])); + assert(p->read.packet); + p->read.data = p->read.packet->data; + } else { + /* Frame is a memblock frame */ + p->read.memblock = pa_memblock_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]), p->memblock_stat); + assert(p->read.memblock); + p->read.data = p->read.memblock->data; + } + + } else if (p->read.index > PA_PSTREAM_DESCRIPTOR_SIZE) { + /* Frame payload available */ + + if (p->read.memblock && p->recieve_memblock_callback) { /* Is this memblock data? Than pass it to the user */ + l = (p->read.index - r) < PA_PSTREAM_DESCRIPTOR_SIZE ? p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE : (size_t) r; + + if (l > 0) { + pa_memchunk chunk; + + chunk.memblock = p->read.memblock; + chunk.index = p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE - l; + chunk.length = l; + + if (p->recieve_memblock_callback) + p->recieve_memblock_callback( + p, + ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]), + ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA]), + &chunk, + p->recieve_memblock_callback_userdata); + } + } + + /* Frame complete */ + if (p->read.index >= ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) + PA_PSTREAM_DESCRIPTOR_SIZE) { + if (p->read.memblock) { + assert(!p->read.packet); + + pa_memblock_unref(p->read.memblock); + p->read.memblock = NULL; + } else { + assert(p->read.packet); + + if (p->recieve_packet_callback) + p->recieve_packet_callback(p, p->read.packet, p->recieve_packet_callback_userdata); + + pa_packet_unref(p->read.packet); + p->read.packet = NULL; + } + + p->read.index = 0; + } + } + + return; + +die: + p->dead = 1; + if (p->die_callback) + p->die_callback(p, p->die_callback_userdata); + +} + +void pa_pstream_set_die_callback(pa_pstream *p, void (*callback)(pa_pstream *p, void *userdata), void *userdata) { + assert(p && callback); + p->die_callback = callback; + p->die_callback_userdata = userdata; +} + +int pa_pstream_is_pending(pa_pstream *p) { + assert(p); + + if (p->dead) + return 0; + + return p->write.current || !pa_queue_is_empty(p->send_queue); +} + +void pa_pstream_set_drain_callback(pa_pstream *p, void (*cb)(pa_pstream *p, void *userdata), void *userdata) { + assert(p); + + p->drain_callback = cb; + p->drain_userdata = userdata; +} + +void pa_pstream_unref(pa_pstream*p) { + assert(p && p->ref >= 1); + + if (!(--(p->ref))) + pstream_free(p); +} + +pa_pstream* pa_pstream_ref(pa_pstream*p) { + assert(p && p->ref >= 1); + p->ref++; + return p; +} + +void pa_pstream_close(pa_pstream *p) { + assert(p); + + p->dead = 1; + + if (p->io) { + pa_iochannel_free(p->io); + p->io = NULL; + } + + if (p->defer_event) { + p->mainloop->defer_free(p->defer_event); + p->defer_event = NULL; + } + + p->die_callback = NULL; + p->drain_callback = NULL; + p->recieve_packet_callback = NULL; + p->recieve_memblock_callback = NULL; +} diff --git a/src/polypcore/pstream.h b/src/polypcore/pstream.h new file mode 100644 index 00000000..ec63a98e --- /dev/null +++ b/src/polypcore/pstream.h @@ -0,0 +1,52 @@ +#ifndef foopstreamhfoo +#define foopstreamhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "packet.h" +#include "memblock.h" +#include "iochannel.h" +#include +#include "memchunk.h" + +typedef struct pa_pstream pa_pstream; + +pa_pstream* pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_memblock_stat *s); +void pa_pstream_unref(pa_pstream*p); +pa_pstream* pa_pstream_ref(pa_pstream*p); + +void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet); +void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk); + +void pa_pstream_set_recieve_packet_callback(pa_pstream *p, void (*callback) (pa_pstream *p, pa_packet *packet, void *userdata), void *userdata); +void pa_pstream_set_recieve_memblock_callback(pa_pstream *p, void (*callback) (pa_pstream *p, uint32_t channel, uint32_t delta, const pa_memchunk *chunk, void *userdata), void *userdata); +void pa_pstream_set_drain_callback(pa_pstream *p, void (*cb)(pa_pstream *p, void *userdata), void *userdata); + +void pa_pstream_set_die_callback(pa_pstream *p, void (*callback)(pa_pstream *p, void *userdata), void *userdata); + +int pa_pstream_is_pending(pa_pstream *p); + +void pa_pstream_close(pa_pstream *p); + +#endif diff --git a/src/polypcore/queue.c b/src/polypcore/queue.c new file mode 100644 index 00000000..80ec0068 --- /dev/null +++ b/src/polypcore/queue.c @@ -0,0 +1,108 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "queue.h" +#include "xmalloc.h" + +struct queue_entry { + struct queue_entry *next; + void *data; +}; + +struct pa_queue { + struct queue_entry *front, *back; + unsigned length; +}; + +pa_queue* pa_queue_new(void) { + pa_queue *q = pa_xnew(pa_queue, 1); + q->front = q->back = NULL; + q->length = 0; + return q; +} + +void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata) { + struct queue_entry *e; + assert(q); + + e = q->front; + while (e) { + struct queue_entry *n = e->next; + + if (destroy) + destroy(e->data, userdata); + + pa_xfree(e); + e = n; + } + + pa_xfree(q); +} + +void pa_queue_push(pa_queue *q, void *p) { + struct queue_entry *e; + + e = pa_xnew(struct queue_entry, 1); + e->data = p; + e->next = NULL; + + if (q->back) + q->back->next = e; + else { + assert(!q->front); + q->front = e; + } + + q->back = e; + q->length++; +} + +void* pa_queue_pop(pa_queue *q) { + void *p; + struct queue_entry *e; + assert(q); + + if (!(e = q->front)) + return NULL; + + q->front = e->next; + if (q->back == e) + q->back = NULL; + + p = e->data; + pa_xfree(e); + + q->length--; + + return p; +} + +int pa_queue_is_empty(pa_queue *q) { + assert(q); + return q->length == 0; +} diff --git a/src/polypcore/queue.h b/src/polypcore/queue.h new file mode 100644 index 00000000..3edcfb63 --- /dev/null +++ b/src/polypcore/queue.h @@ -0,0 +1,40 @@ +#ifndef fooqueuehfoo +#define fooqueuehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct pa_queue pa_queue; + +/* A simple implementation of the abstract data type queue. Stores + * pointers as members. The memory has to be managed by the caller. */ + +pa_queue* pa_queue_new(void); + +/* Free the queue and run the specified callback function for every remaining entry. The callback function may be NULL. */ +void pa_queue_free(pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata); + +void pa_queue_push(pa_queue *q, void *p); +void* pa_queue_pop(pa_queue *q); + +int pa_queue_is_empty(pa_queue *q); + +#endif diff --git a/src/polypcore/random.c b/src/polypcore/random.c new file mode 100644 index 00000000..12f27bfd --- /dev/null +++ b/src/polypcore/random.c @@ -0,0 +1,71 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "random.h" +#include "util.h" +#include "log.h" + +#ifndef OS_IS_WIN32 +#define RANDOM_DEVICE "/dev/urandom" +#endif + +void pa_random(void *ret_data, size_t length) { + int fd; + ssize_t r = 0; + assert(ret_data && length); + +#ifdef RANDOM_DEVICE + if ((fd = open(RANDOM_DEVICE, O_RDONLY)) >= 0) { + + if ((r = pa_loop_read(fd, ret_data, length)) < 0 || (size_t) r != length) + pa_log_error(__FILE__": failed to read entropy from '%s'\n", RANDOM_DEVICE); + + close(fd); + } +#endif + + if ((size_t) r != length) { + uint8_t *p; + size_t l; + +#ifdef RANDOM_DEVICE + pa_log_warn(__FILE__": WARNING: Failed to open entropy device '"RANDOM_DEVICE"': %s" + ", falling back to unsecure pseudo RNG.\n", strerror(errno)); +#endif + + srand(time(NULL)); + + for (p = ret_data, l = length; l > 0; p++, l--) + *p = (uint8_t) rand(); + } +} diff --git a/src/polypcore/random.h b/src/polypcore/random.h new file mode 100644 index 00000000..bfb3df08 --- /dev/null +++ b/src/polypcore/random.h @@ -0,0 +1,27 @@ +#ifndef foorandomhfoo +#define foorandomhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +void pa_random(void *ret_data, size_t length); + +#endif diff --git a/src/polypcore/resampler.c b/src/polypcore/resampler.c new file mode 100644 index 00000000..0417e44e --- /dev/null +++ b/src/polypcore/resampler.c @@ -0,0 +1,614 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include +#include + +#include "resampler.h" +#include "sconv.h" +#include "xmalloc.h" +#include "log.h" + +struct pa_resampler { + pa_resample_method_t resample_method; + pa_sample_spec i_ss, o_ss; + pa_channel_map i_cm, o_cm; + size_t i_fz, o_fz; + pa_memblock_stat *memblock_stat; + + void (*impl_free)(pa_resampler *r); + void (*impl_update_input_rate)(pa_resampler *r, uint32_t rate); + void (*impl_run)(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out); + void *impl_data; +}; + +struct impl_libsamplerate { + float* buf1, *buf2, *buf3, *buf4; + unsigned buf1_samples, buf2_samples, buf3_samples, buf4_samples; + + pa_convert_to_float32ne_func_t to_float32ne_func; + pa_convert_from_float32ne_func_t from_float32ne_func; + SRC_STATE *src_state; + + int map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX]; + int map_required; +}; + +struct impl_trivial { + unsigned o_counter; + unsigned i_counter; +}; + +static int libsamplerate_init(pa_resampler*r); +static int trivial_init(pa_resampler*r); + +pa_resampler* pa_resampler_new( + const pa_sample_spec *a, + const pa_channel_map *am, + const pa_sample_spec *b, + const pa_channel_map *bm, + pa_memblock_stat *s, + pa_resample_method_t resample_method) { + + pa_resampler *r = NULL; + + assert(a); + assert(b); + assert(pa_sample_spec_valid(a)); + assert(pa_sample_spec_valid(b)); + assert(resample_method != PA_RESAMPLER_INVALID); + + r = pa_xnew(pa_resampler, 1); + r->impl_data = NULL; + r->memblock_stat = s; + r->resample_method = resample_method; + + r->impl_free = NULL; + r->impl_update_input_rate = NULL; + r->impl_run = NULL; + + /* Fill sample specs */ + r->i_ss = *a; + r->o_ss = *b; + + if (am) + r->i_cm = *am; + else + pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels); + + if (bm) + r->o_cm = *bm; + else + pa_channel_map_init_auto(&r->o_cm, r->o_ss.channels); + + r->i_fz = pa_frame_size(a); + r->o_fz = pa_frame_size(b); + + /* Choose implementation */ + if (a->channels != b->channels || + a->format != b->format || + !pa_channel_map_equal(&r->i_cm, &r->o_cm) || + resample_method != PA_RESAMPLER_TRIVIAL) { + + /* Use the libsamplerate based resampler for the complicated cases */ + if (resample_method == PA_RESAMPLER_TRIVIAL) + r->resample_method = PA_RESAMPLER_SRC_ZERO_ORDER_HOLD; + + if (libsamplerate_init(r) < 0) + goto fail; + + } else { + /* Use our own simple non-fp resampler for the trivial cases and when the user selects it */ + if (trivial_init(r) < 0) + goto fail; + } + + return r; + +fail: + if (r) + pa_xfree(r); + + return NULL; +} + +void pa_resampler_free(pa_resampler *r) { + assert(r); + + if (r->impl_free) + r->impl_free(r); + + pa_xfree(r); +} + +void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate) { + assert(r); + assert(rate > 0); + + if (r->i_ss.rate == rate) + return; + + r->i_ss.rate = rate; + + if (r->impl_update_input_rate) + r->impl_update_input_rate(r, rate); +} + +void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) { + assert(r && in && out && r->impl_run); + + r->impl_run(r, in, out); +} + +size_t pa_resampler_request(pa_resampler *r, size_t out_length) { + assert(r && (out_length % r->o_fz) == 0); + return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz; +} + +pa_resample_method_t pa_resampler_get_method(pa_resampler *r) { + assert(r); + return r->resample_method; +} + +static const char * const resample_methods[] = { + "src-sinc-best-quality", + "src-sinc-medium-quality", + "src-sinc-fastest", + "src-zero-order-hold", + "src-linear", + "trivial" +}; + +const char *pa_resample_method_to_string(pa_resample_method_t m) { + + if (m < 0 || m >= PA_RESAMPLER_MAX) + return NULL; + + return resample_methods[m]; +} + +pa_resample_method_t pa_parse_resample_method(const char *string) { + pa_resample_method_t m; + + assert(string); + + for (m = 0; m < PA_RESAMPLER_MAX; m++) + if (!strcmp(string, resample_methods[m])) + return m; + + return PA_RESAMPLER_INVALID; +} + + +/*** libsamplerate based implementation ***/ + +static void libsamplerate_free(pa_resampler *r) { + struct impl_libsamplerate *u; + + assert(r); + assert(r->impl_data); + + u = r->impl_data; + + if (u->src_state) + src_delete(u->src_state); + + pa_xfree(u->buf1); + pa_xfree(u->buf2); + pa_xfree(u->buf3); + pa_xfree(u->buf4); + pa_xfree(u); +} + +static void calc_map_table(pa_resampler *r) { + struct impl_libsamplerate *u; + unsigned oc; + assert(r); + assert(r->impl_data); + + u = r->impl_data; + + if (!(u->map_required = (!pa_channel_map_equal(&r->i_cm, &r->o_cm) || r->i_ss.channels != r->o_ss.channels))) + return; + + for (oc = 0; oc < r->o_ss.channels; oc++) { + unsigned ic, i = 0; + + for (ic = 0; ic < r->i_ss.channels; ic++) { + pa_channel_position_t a, b; + + a = r->i_cm.map[ic]; + b = r->o_cm.map[oc]; + + if (a == b || + (a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_LEFT) || + (a == PA_CHANNEL_POSITION_MONO && b == PA_CHANNEL_POSITION_RIGHT) || + (a == PA_CHANNEL_POSITION_LEFT && b == PA_CHANNEL_POSITION_MONO) || + (a == PA_CHANNEL_POSITION_RIGHT && b == PA_CHANNEL_POSITION_MONO)) + + u->map_table[oc][i++] = ic; + } + + /* Add an end marker */ + if (i < PA_CHANNELS_MAX) + u->map_table[oc][i] = -1; + } +} + +static float * convert_to_float(pa_resampler *r, float *input, unsigned n_frames) { + struct impl_libsamplerate *u; + unsigned n_samples; + + assert(r); + assert(input); + assert(r->impl_data); + u = r->impl_data; + + /* Convert the incoming sample into floats and place them in buf1 */ + + if (!u->to_float32ne_func) + return input; + + n_samples = n_frames * r->i_ss.channels; + + if (u->buf1_samples < n_samples) + u->buf1 = pa_xrealloc(u->buf1, sizeof(float) * (u->buf1_samples = n_samples)); + + u->to_float32ne_func(n_samples, input, u->buf1); + + return u->buf1; +} + +static float *remap_channels(pa_resampler *r, float *input, unsigned n_frames) { + struct impl_libsamplerate *u; + unsigned n_samples; + int i_skip, o_skip; + unsigned oc; + + assert(r); + assert(input); + assert(r->impl_data); + u = r->impl_data; + + /* Remap channels and place the result int buf2 */ + + if (!u->map_required) + return input; + + n_samples = n_frames * r->o_ss.channels; + + if (u->buf2_samples < n_samples) + u->buf2 = pa_xrealloc(u->buf2, sizeof(float) * (u->buf2_samples = n_samples)); + + memset(u->buf2, 0, n_samples * sizeof(float)); + + o_skip = sizeof(float) * r->o_ss.channels; + i_skip = sizeof(float) * r->i_ss.channels; + + for (oc = 0; oc < r->o_ss.channels; oc++) { + unsigned i; + static const float one = 1.0; + + for (i = 0; i < PA_CHANNELS_MAX && u->map_table[oc][i] >= 0; i++) + oil_vectoradd_f32( + u->buf2 + oc, o_skip, + u->buf2 + oc, o_skip, + input + u->map_table[oc][i], i_skip, + n_frames, + &one, &one); + } + + return u->buf2; +} + +static float *resample(pa_resampler *r, float *input, unsigned *n_frames) { + struct impl_libsamplerate *u; + SRC_DATA data; + unsigned out_n_frames, out_n_samples; + int ret; + + assert(r); + assert(input); + assert(n_frames); + assert(r->impl_data); + u = r->impl_data; + + /* Resample the data and place the result in buf3 */ + + if (!u->src_state) + return input; + + out_n_frames = (*n_frames*r->o_ss.rate/r->i_ss.rate)+1024; + out_n_samples = out_n_frames * r->o_ss.channels; + + if (u->buf3_samples < out_n_samples) + u->buf3 = pa_xrealloc(u->buf3, sizeof(float) * (u->buf3_samples = out_n_samples)); + + data.data_in = input; + data.input_frames = *n_frames; + + data.data_out = u->buf3; + data.output_frames = out_n_frames; + + data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate; + data.end_of_input = 0; + + ret = src_process(u->src_state, &data); + assert(ret == 0); + assert((unsigned) data.input_frames_used == *n_frames); + + *n_frames = data.output_frames_gen; + + return u->buf3; +} + +static float *convert_from_float(pa_resampler *r, float *input, unsigned n_frames) { + struct impl_libsamplerate *u; + unsigned n_samples; + + assert(r); + assert(input); + assert(r->impl_data); + u = r->impl_data; + + /* Convert the data into the correct sample type and place the result in buf4 */ + + if (!u->from_float32ne_func) + return input; + + n_samples = n_frames * r->o_ss.channels; + + if (u->buf4_samples < n_samples) + u->buf4 = pa_xrealloc(u->buf4, sizeof(float) * (u->buf4_samples = n_samples)); + + u->from_float32ne_func(n_samples, input, u->buf4); + + return u->buf4; +} + +static void libsamplerate_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) { + struct impl_libsamplerate *u; + float *buf, *input; + unsigned n_frames; + + assert(r); + assert(in); + assert(out); + assert(in->length); + assert(in->memblock); + assert(in->length % r->i_fz == 0); + assert(r->impl_data); + + u = r->impl_data; + + buf = input = (float*) ((uint8_t*) in->memblock->data + in->index); + n_frames = in->length / r->i_fz; + assert(n_frames > 0); + + buf = convert_to_float(r, buf, n_frames); + buf = remap_channels(r, buf, n_frames); + buf = resample(r, buf, &n_frames); + + if (n_frames) { + buf = convert_from_float(r, buf, n_frames); + + if (buf == input) { + /* Mm, no adjustment has been necessary, so let's return the original block */ + out->memblock = pa_memblock_ref(in->memblock); + out->index = in->index; + out->length = in->length; + } else { + float **p = NULL; + + out->length = n_frames * r->o_fz; + out->index = 0; + + if (buf == u->buf1) { + p = &u->buf1; + u->buf1_samples = 0; + } else if (buf == u->buf2) { + p = &u->buf2; + u->buf2_samples = 0; + } else if (buf == u->buf3) { + p = &u->buf3; + u->buf3_samples = 0; + } else if (buf == u->buf4) { + p = &u->buf4; + u->buf4_samples = 0; + } + + assert(p); + + /* Take the existing buffer and make it a memblock */ + out->memblock = pa_memblock_new_dynamic(*p, out->length, r->memblock_stat); + *p = NULL; + } + } else { + out->memblock = NULL; + out->index = out->length = 0; + } +} + +static void libsamplerate_update_input_rate(pa_resampler *r, uint32_t rate) { + struct impl_libsamplerate *u; + + assert(r); + assert(rate > 0); + assert(r->impl_data); + u = r->impl_data; + + if (!u->src_state) { + int err; + u->src_state = src_new(r->resample_method, r->o_ss.channels, &err); + assert(u->src_state); + } else { + int ret = src_set_ratio(u->src_state, (double) r->o_ss.rate / rate); + assert(ret == 0); + } +} + +static int libsamplerate_init(pa_resampler *r) { + struct impl_libsamplerate *u = NULL; + int err; + + r->impl_data = u = pa_xnew(struct impl_libsamplerate, 1); + + u->buf1 = u->buf2 = u->buf3 = u->buf4 = NULL; + u->buf1_samples = u->buf2_samples = u->buf3_samples = u->buf4_samples = 0; + + if (r->i_ss.format == PA_SAMPLE_FLOAT32NE) + u->to_float32ne_func = NULL; + else if (!(u->to_float32ne_func = pa_get_convert_to_float32ne_function(r->i_ss.format))) + goto fail; + + if (r->o_ss.format == PA_SAMPLE_FLOAT32NE) + u->from_float32ne_func = NULL; + else if (!(u->from_float32ne_func = pa_get_convert_from_float32ne_function(r->o_ss.format))) + goto fail; + + if (r->o_ss.rate == r->i_ss.rate) + u->src_state = NULL; + else if (!(u->src_state = src_new(r->resample_method, r->o_ss.channels, &err))) + goto fail; + + r->impl_free = libsamplerate_free; + r->impl_update_input_rate = libsamplerate_update_input_rate; + r->impl_run = libsamplerate_run; + + calc_map_table(r); + + return 0; + +fail: + pa_xfree(u); + return -1; +} + +/* Trivial implementation */ + +static void trivial_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) { + size_t fz; + unsigned n_frames; + struct impl_trivial *u; + + assert(r); + assert(in); + assert(out); + assert(r->impl_data); + + u = r->impl_data; + + fz = r->i_fz; + assert(fz == r->o_fz); + + n_frames = in->length/fz; + + if (r->i_ss.rate == r->o_ss.rate) { + + /* In case there's no diefference in sample types, do nothing */ + *out = *in; + pa_memblock_ref(out->memblock); + + u->o_counter += n_frames; + } else { + /* Do real resampling */ + size_t l; + unsigned o_index; + + /* The length of the new memory block rounded up */ + l = ((((n_frames+1) * r->o_ss.rate) / r->i_ss.rate) + 1) * fz; + + out->index = 0; + out->memblock = pa_memblock_new(l, r->memblock_stat); + + for (o_index = 0;; o_index++, u->o_counter++) { + unsigned j; + + j = (u->o_counter * r->i_ss.rate / r->o_ss.rate); + j = j > u->i_counter ? j - u->i_counter : 0; + + if (j >= n_frames) + break; + + assert(o_index*fz < out->memblock->length); + + memcpy((uint8_t*) out->memblock->data + fz*o_index, + (uint8_t*) in->memblock->data + in->index + fz*j, fz); + + } + + out->length = o_index*fz; + } + + u->i_counter += n_frames; + + /* Normalize counters */ + while (u->i_counter >= r->i_ss.rate) { + u->i_counter -= r->i_ss.rate; + assert(u->o_counter >= r->o_ss.rate); + u->o_counter -= r->o_ss.rate; + } +} + +static void trivial_free(pa_resampler *r) { + assert(r); + + pa_xfree(r->impl_data); +} + +static void trivial_update_input_rate(pa_resampler *r, uint32_t rate) { + struct impl_trivial *u; + + assert(r); + assert(rate > 0); + assert(r->impl_data); + + u = r->impl_data; + u->i_counter = 0; + u->o_counter = 0; +} + +static int trivial_init(pa_resampler*r) { + struct impl_trivial *u; + + assert(r); + assert(r->i_ss.format == r->o_ss.format); + assert(r->i_ss.channels == r->o_ss.channels); + + r->impl_data = u = pa_xnew(struct impl_trivial, 1); + u->o_counter = u->i_counter = 0; + + r->impl_run = trivial_run; + r->impl_free = trivial_free; + r->impl_update_input_rate = trivial_update_input_rate; + + return 0; +} + + diff --git a/src/polypcore/resampler.h b/src/polypcore/resampler.h new file mode 100644 index 00000000..cad48be5 --- /dev/null +++ b/src/polypcore/resampler.h @@ -0,0 +1,73 @@ +#ifndef fooresamplerhfoo +#define fooresamplerhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include "memblock.h" +#include "memchunk.h" +#include + +typedef struct pa_resampler pa_resampler; + +typedef enum pa_resample_method { + PA_RESAMPLER_INVALID = -1, + PA_RESAMPLER_SRC_SINC_BEST_QUALITY = SRC_SINC_BEST_QUALITY, + PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY = SRC_SINC_MEDIUM_QUALITY, + PA_RESAMPLER_SRC_SINC_FASTEST = SRC_SINC_FASTEST, + PA_RESAMPLER_SRC_ZERO_ORDER_HOLD = SRC_ZERO_ORDER_HOLD, + PA_RESAMPLER_SRC_LINEAR = SRC_LINEAR, + PA_RESAMPLER_TRIVIAL, + PA_RESAMPLER_MAX +} pa_resample_method_t; + +pa_resampler* pa_resampler_new( + const pa_sample_spec *a, + const pa_channel_map *am, + const pa_sample_spec *b, + const pa_channel_map *bm, + pa_memblock_stat *s, + pa_resample_method_t resample_method); + +void pa_resampler_free(pa_resampler *r); + +/* Returns the size of an input memory block which is required to return the specified amount of output data */ +size_t pa_resampler_request(pa_resampler *r, size_t out_length); + +/* Pass the specified memory chunk to the resampler and return the newly resampled data */ +void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out); + +/* Change the input rate of the resampler object */ +void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate); + +/* Return the resampling method of the resampler object */ +pa_resample_method_t pa_resampler_get_method(pa_resampler *r); + +/* Try to parse the resampler method */ +pa_resample_method_t pa_parse_resample_method(const char *string); + +/* return a human readable string for the specified resampling method. Inverse of pa_parse_resample_method() */ +const char *pa_resample_method_to_string(pa_resample_method_t m); + +#endif diff --git a/src/polypcore/sample-util.c b/src/polypcore/sample-util.c new file mode 100644 index 00000000..52974c46 --- /dev/null +++ b/src/polypcore/sample-util.c @@ -0,0 +1,328 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include + +#include "log.h" +#include "sample-util.h" + +pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) { + assert(b && b->data && spec); + pa_silence_memory(b->data, b->length, spec); + return b; +} + +void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) { + assert(c && c->memblock && c->memblock->data && spec && c->length); + + pa_silence_memory((uint8_t*) c->memblock->data+c->index, c->length, spec); +} + +void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) { + uint8_t c = 0; + assert(p && length && spec); + + switch (spec->format) { + case PA_SAMPLE_U8: + c = 0x80; + break; + case PA_SAMPLE_S16LE: + case PA_SAMPLE_S16BE: + case PA_SAMPLE_FLOAT32: + c = 0; + break; + case PA_SAMPLE_ALAW: + case PA_SAMPLE_ULAW: + c = 80; + break; + default: + assert(0); + } + + memset(p, c, length); +} + +size_t pa_mix( + const pa_mix_info streams[], + unsigned nstreams, + void *data, + size_t length, + const pa_sample_spec *spec, + const pa_cvolume *volume) { + + assert(streams && data && length && spec); + + switch (spec->format) { + case PA_SAMPLE_S16NE:{ + size_t d; + unsigned channel = 0; + + for (d = 0;; d += sizeof(int16_t)) { + int32_t sum = 0; + + if (d >= length) + return d; + + if (volume->values[channel] != PA_VOLUME_MUTED) { + unsigned i; + + for (i = 0; i < nstreams; i++) { + int32_t v; + pa_volume_t cvolume = streams[i].volume.values[channel]; + + if (d >= streams[i].chunk.length) + return d; + + if (cvolume == PA_VOLUME_MUTED) + v = 0; + else { + v = *((int16_t*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d)); + + if (cvolume != PA_VOLUME_NORM) { + v *= cvolume; + v /= PA_VOLUME_NORM; + } + } + + sum += v; + } + + if (volume->values[channel] != PA_VOLUME_NORM) { + sum *= volume->values[channel]; + sum /= PA_VOLUME_NORM; + } + + if (sum < -0x8000) sum = -0x8000; + if (sum > 0x7FFF) sum = 0x7FFF; + + } + + *((int16_t*) data) = sum; + data = (uint8_t*) data + sizeof(int16_t); + + if (++channel >= spec->channels) + channel = 0; + } + } + + case PA_SAMPLE_U8: { + size_t d; + unsigned channel = 0; + + for (d = 0;; d ++) { + int32_t sum = 0; + + if (d >= length) + return d; + + if (volume->values[channel] != PA_VOLUME_MUTED) { + unsigned i; + + for (i = 0; i < nstreams; i++) { + int32_t v; + pa_volume_t cvolume = streams[i].volume.values[channel]; + + if (d >= streams[i].chunk.length) + return d; + + if (cvolume == PA_VOLUME_MUTED) + v = 0; + else { + v = (int32_t) *((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d) - 0x80; + + if (cvolume != PA_VOLUME_NORM) { + v *= cvolume; + v /= PA_VOLUME_NORM; + } + } + + sum += v; + } + + if (volume->values[channel] != PA_VOLUME_NORM) { + sum *= volume->values[channel]; + sum /= PA_VOLUME_NORM; + } + + if (sum < -0x80) sum = -0x80; + if (sum > 0x7F) sum = 0x7F; + + } + + *((uint8_t*) data) = (uint8_t) (sum + 0x80); + data = (uint8_t*) data + 1; + + if (++channel >= spec->channels) + channel = 0; + } + } + + case PA_SAMPLE_FLOAT32NE: { + size_t d; + unsigned channel = 0; + + for (d = 0;; d += sizeof(float)) { + float sum = 0; + + if (d >= length) + return d; + + if (volume->values[channel] != PA_VOLUME_MUTED) { + unsigned i; + + for (i = 0; i < nstreams; i++) { + float v; + pa_volume_t cvolume = streams[i].volume.values[channel]; + + if (d >= streams[i].chunk.length) + return d; + + if (cvolume == PA_VOLUME_MUTED) + v = 0; + else { + v = *((float*) ((uint8_t*) streams[i].chunk.memblock->data + streams[i].chunk.index + d)); + + if (cvolume != PA_VOLUME_NORM) { + v *= cvolume; + v /= PA_VOLUME_NORM; + } + } + + sum += v; + } + + if (volume->values[channel] != PA_VOLUME_NORM) { + sum *= volume->values[channel]; + sum /= PA_VOLUME_NORM; + } + } + + *((float*) data) = sum; + data = (uint8_t*) data + sizeof(float); + + if (++channel >= spec->channels) + channel = 0; + } + } + + default: + abort(); + } +} + + +void pa_volume_memchunk(pa_memchunk*c, const pa_sample_spec *spec, const pa_cvolume *volume) { + assert(c && spec && (c->length % pa_frame_size(spec) == 0)); + assert(volume); + + if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_NORM)) + return; + + if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_MUTED)) { + pa_silence_memchunk(c, spec); + return; + } + + switch (spec->format) { + case PA_SAMPLE_S16NE: { + int16_t *d; + size_t n; + unsigned channel = 0; + + for (d = (int16_t*) ((uint8_t*) c->memblock->data+c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) { + int32_t t = (int32_t)(*d); + + t *= volume->values[channel]; + t /= PA_VOLUME_NORM; + + if (t < -0x8000) t = -0x8000; + if (t > 0x7FFF) t = 0x7FFF; + + *d = (int16_t) t; + + if (++channel >= spec->channels) + channel = 0; + } + break; + } + + case PA_SAMPLE_U8: { + uint8_t *d; + size_t n; + unsigned channel = 0; + + for (d = (uint8_t*) c->memblock->data + c->index, n = c->length; n > 0; d++, n--) { + int32_t t = (int32_t) *d - 0x80; + + t *= volume->values[channel]; + t /= PA_VOLUME_NORM; + + if (t < -0x80) t = -0x80; + if (t > 0x7F) t = 0x7F; + + *d = (uint8_t) (t + 0x80); + + if (++channel >= spec->channels) + channel = 0; + } + break; + } + + case PA_SAMPLE_FLOAT32NE: { + float *d; + int skip; + unsigned n; + unsigned channel; + + d = (float*) ((uint8_t*) c->memblock->data + c->index); + skip = spec->channels * sizeof(float); + n = c->length/sizeof(float)/spec->channels; + + for (channel = 0; channel < spec->channels ; channel ++) { + float v, *t; + + if (volume->values[channel] == PA_VOLUME_NORM) + continue; + + v = (float) volume->values[channel] / PA_VOLUME_NORM; + + t = d + channel; + oil_scalarmult_f32(t, skip, t, skip, &v, n); + } + break; + } + + default: + pa_log_error(__FILE__": ERROR: Unable to change volume of format %s.\n", + pa_sample_format_to_string(spec->format)); + abort(); + } +} + diff --git a/src/polypcore/sample-util.h b/src/polypcore/sample-util.h new file mode 100644 index 00000000..d0efbcc4 --- /dev/null +++ b/src/polypcore/sample-util.h @@ -0,0 +1,53 @@ +#ifndef foosampleutilhfoo +#define foosampleutilhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include "memblock.h" +#include "memchunk.h" +#include + +pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec); +void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec); +void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec); + +typedef struct pa_mix_info { + pa_memchunk chunk; + pa_cvolume volume; + void *userdata; +} pa_mix_info; + +size_t pa_mix( + const pa_mix_info channels[], + unsigned nchannels, + void *data, + size_t length, + const pa_sample_spec *spec, + const pa_cvolume *volume); + +void pa_volume_memchunk( + pa_memchunk*c, + const pa_sample_spec *spec, + const pa_cvolume *volume); + +#endif diff --git a/src/polypcore/scache.c b/src/polypcore/scache.c new file mode 100644 index 00000000..02c7d34f --- /dev/null +++ b/src/polypcore/scache.c @@ -0,0 +1,392 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_GLOB_H +#include +#endif + +#ifdef HAVE_WINDOWS_H +#include +#endif + +#include "scache.h" +#include "sink-input.h" +#include +#include "sample-util.h" +#include "play-memchunk.h" +#include "xmalloc.h" +#include "subscribe.h" +#include "namereg.h" +#include "sound-file.h" +#include "util.h" +#include "log.h" +#include +#include + +#define UNLOAD_POLL_TIME 2 + +static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { + pa_core *c = userdata; + struct timeval ntv; + assert(c && c->mainloop == m && c->scache_auto_unload_event == e); + + pa_scache_unload_unused(c); + + pa_gettimeofday(&ntv); + ntv.tv_sec += UNLOAD_POLL_TIME; + m->time_restart(e, &ntv); +} + +static void free_entry(pa_scache_entry *e) { + assert(e); + pa_namereg_unregister(e->core, e->name); + pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_REMOVE, e->index); + pa_xfree(e->name); + pa_xfree(e->filename); + if (e->memchunk.memblock) + pa_memblock_unref(e->memchunk.memblock); + pa_xfree(e); +} + +static pa_scache_entry* scache_add_item(pa_core *c, const char *name) { + pa_scache_entry *e; + assert(c && name); + + if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) { + if (e->memchunk.memblock) + pa_memblock_unref(e->memchunk.memblock); + + pa_xfree(e->filename); + + assert(e->core == c); + + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); + } else { + e = pa_xmalloc(sizeof(pa_scache_entry)); + + if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, 1)) { + pa_xfree(e); + return NULL; + } + + e->name = pa_xstrdup(name); + e->core = c; + + if (!c->scache) { + c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + assert(c->scache); + } + + pa_idxset_put(c->scache, e, &e->index); + + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_NEW, e->index); + } + + pa_cvolume_reset(&e->volume, PA_CHANNELS_MAX); + e->last_used_time = 0; + e->memchunk.memblock = NULL; + e->memchunk.index = e->memchunk.length = 0; + e->filename = NULL; + e->lazy = 0; + e->last_used_time = 0; + + memset(&e->sample_spec, 0, sizeof(pa_sample_spec)); + + return e; +} + +int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx) { + pa_scache_entry *e; + assert(c && name); + + if (!(e = scache_add_item(c, name))) + return -1; + + if (ss) { + e->sample_spec = *ss; + pa_channel_map_init_auto(&e->channel_map, ss->channels); + } + + if (map) + e->channel_map = *map; + + if (chunk) { + e->memchunk = *chunk; + pa_memblock_ref(e->memchunk.memblock); + } + + if (idx) + *idx = e->index; + + return 0; +} + +int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx) { + pa_sample_spec ss; + pa_memchunk chunk; + int r; + +#ifdef OS_IS_WIN32 + char buf[MAX_PATH]; + + if (ExpandEnvironmentStrings(filename, buf, MAX_PATH)) + filename = buf; +#endif + + if (pa_sound_file_load(filename, &ss, &chunk, c->memblock_stat) < 0) + return -1; + + r = pa_scache_add_item(c, name, &ss, NULL, &chunk, idx); + pa_memblock_unref(chunk.memblock); + + return r; +} + +int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx) { + pa_scache_entry *e; + +#ifdef OS_IS_WIN32 + char buf[MAX_PATH]; + + if (ExpandEnvironmentStrings(filename, buf, MAX_PATH)) + filename = buf; +#endif + + assert(c && name); + + if (!(e = scache_add_item(c, name))) + return -1; + + e->lazy = 1; + e->filename = pa_xstrdup(filename); + + if (!c->scache_auto_unload_event) { + struct timeval ntv; + pa_gettimeofday(&ntv); + ntv.tv_sec += UNLOAD_POLL_TIME; + c->scache_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c); + } + + if (idx) + *idx = e->index; + + return 0; +} + +int pa_scache_remove_item(pa_core *c, const char *name) { + pa_scache_entry *e; + assert(c && name); + + if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) + return -1; + + if (pa_idxset_remove_by_data(c->scache, e, NULL) != e) + assert(0); + + free_entry(e); + return 0; +} + +static void free_cb(void *p, PA_GCC_UNUSED void *userdata) { + pa_scache_entry *e = p; + assert(e); + free_entry(e); +} + +void pa_scache_free(pa_core *c) { + assert(c); + + if (c->scache) { + pa_idxset_free(c->scache, free_cb, NULL); + c->scache = NULL; + } + + if (c->scache_auto_unload_event) + c->mainloop->time_free(c->scache_auto_unload_event); +} + +int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, const pa_cvolume *volume) { + pa_scache_entry *e; + char *t; + pa_cvolume r; + assert(c && name && sink); + + if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 1))) + return -1; + + if (e->lazy && !e->memchunk.memblock) { + if (pa_sound_file_load(e->filename, &e->sample_spec, &e->memchunk, c->memblock_stat) < 0) + return -1; + + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); + } + + if (!e->memchunk.memblock) + return -1; + + t = pa_sprintf_malloc("sample:%s", name); + + if (pa_play_memchunk(sink, t, &e->sample_spec, &e->channel_map, &e->memchunk, pa_sw_cvolume_multiply(&r, volume, &e->volume)) < 0) { + free(t); + return -1; + } + + free(t); + + if (e->lazy) + time(&e->last_used_time); + + return 0; +} + +const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) { + pa_scache_entry *e; + assert(c && id != PA_IDXSET_INVALID); + + if (!c->scache || !(e = pa_idxset_get_by_index(c->scache, id))) + return NULL; + + return e->name; +} + +uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) { + pa_scache_entry *e; + assert(c && name); + + if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0))) + return PA_IDXSET_INVALID; + + return e->index; +} + +uint32_t pa_scache_total_size(pa_core *c) { + pa_scache_entry *e; + uint32_t idx, sum = 0; + assert(c); + + if (!c->scache || !pa_idxset_size(c->scache)) + return 0; + + for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) + if (e->memchunk.memblock) + sum += e->memchunk.length; + + return sum; +} + +void pa_scache_unload_unused(pa_core *c) { + pa_scache_entry *e; + time_t now; + uint32_t idx; + assert(c); + + if (!c->scache || !pa_idxset_size(c->scache)) + return; + + time(&now); + + for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) { + + if (!e->lazy || !e->memchunk.memblock) + continue; + + if (e->last_used_time + c->scache_idle_time > now) + continue; + + pa_memblock_unref(e->memchunk.memblock); + e->memchunk.memblock = NULL; + e->memchunk.index = e->memchunk.length = 0; + + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); + } +} + +static void add_file(pa_core *c, const char *pathname) { + struct stat st; + const char *e; + + e = pa_path_get_filename(pathname); + + if (stat(pathname, &st) < 0) { + pa_log(__FILE__": stat('%s') failed: %s\n", pathname, strerror(errno)); + return; + } + +#if defined(S_ISREG) && defined(S_ISLNK) + if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) +#endif + pa_scache_add_file_lazy(c, e, pathname, NULL); +} + +int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) { + DIR *dir; + assert(c && pathname); + + /* First try to open this as directory */ + if (!(dir = opendir(pathname))) { +#ifdef HAVE_GLOB_H + glob_t p; + unsigned int i; + /* If that fails, try to open it as shell glob */ + + if (glob(pathname, GLOB_ERR|GLOB_NOSORT, NULL, &p) < 0) { + pa_log(__FILE__": Failed to open directory: %s\n", strerror(errno)); + return -1; + } + + for (i = 0; i < p.gl_pathc; i++) + add_file(c, p.gl_pathv[i]); + + globfree(&p); +#else + return -1; +#endif + } else { + struct dirent *e; + + while ((e = readdir(dir))) { + char p[PATH_MAX]; + + if (e->d_name[0] == '.') + continue; + + snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name); + add_file(c, p); + } + } + + closedir(dir); + return 0; +} diff --git a/src/polypcore/scache.h b/src/polypcore/scache.h new file mode 100644 index 00000000..d667ae60 --- /dev/null +++ b/src/polypcore/scache.h @@ -0,0 +1,62 @@ +#ifndef fooscachehfoo +#define fooscachehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "core.h" +#include "memchunk.h" +#include "sink.h" + +typedef struct pa_scache_entry { + pa_core *core; + uint32_t index; + char *name; + + pa_cvolume volume; + pa_sample_spec sample_spec; + pa_channel_map channel_map; + pa_memchunk memchunk; + + char *filename; + + int lazy; + time_t last_used_time; +} pa_scache_entry; + +int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx); +int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx); +int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx); + +int pa_scache_add_directory_lazy(pa_core *c, const char *pathname); + +int pa_scache_remove_item(pa_core *c, const char *name); +int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, const pa_cvolume *cvolume); +void pa_scache_free(pa_core *c); + +const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id); +uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name); + +uint32_t pa_scache_total_size(pa_core *c); + +void pa_scache_unload_unused(pa_core *c); + +#endif diff --git a/src/polypcore/sconv-s16be.c b/src/polypcore/sconv-s16be.c new file mode 100644 index 00000000..8b076f06 --- /dev/null +++ b/src/polypcore/sconv-s16be.c @@ -0,0 +1,41 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "endianmacros.h" + +#define INT16_FROM INT16_FROM_BE +#define INT16_TO INT16_TO_BE + +#define pa_sconv_s16le_to_float32ne pa_sconv_s16be_to_float32ne +#define pa_sconv_s16le_from_float32ne pa_sconv_s16be_from_float32ne + +#ifdef WORDS_BIGENDIAN +#define SWAP_WORDS 0 +#else +#define SWAP_WORDS 1 +#endif + +#include "sconv-s16le.h" +#include "sconv-s16le.c" diff --git a/src/polypcore/sconv-s16be.h b/src/polypcore/sconv-s16be.h new file mode 100644 index 00000000..b2b6a8c7 --- /dev/null +++ b/src/polypcore/sconv-s16be.h @@ -0,0 +1,28 @@ +#ifndef foosconv_s16befoo +#define foosconv_s16befoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +void pa_sconv_s16be_to_float32ne(unsigned n, const void *a, float *b); +void pa_sconv_s16be_from_float32ne(unsigned n, const float *a, void *b); + +#endif diff --git a/src/polypcore/sconv-s16le.c b/src/polypcore/sconv-s16le.c new file mode 100644 index 00000000..e47c5e8e --- /dev/null +++ b/src/polypcore/sconv-s16le.c @@ -0,0 +1,101 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +#include "endianmacros.h" +#include "sconv.h" +#include "sconv-s16le.h" +#include "log.h" + +#ifndef INT16_FROM +#define INT16_FROM INT16_FROM_LE +#endif + +#ifndef INT16_TO +#define INT16_TO INT16_TO_LE +#endif + +#ifndef SWAP_WORDS +#ifdef WORDS_BIGENDIAN +#define SWAP_WORDS 1 +#else +#define SWAP_WORDS 0 +#endif +#endif + +void pa_sconv_s16le_to_float32ne(unsigned n, const void *a, float *b) { + const int16_t *ca = a; + + assert(a); + assert(b); + +#if SWAP_WORDS == 1 + + for (; n > 0; n--) { + int16_t s = *(ca++); + *(b++) = ((float) INT16_FROM(s))/0x7FFF; + } + +#else +{ + static const double add = 0, factor = 1.0/0x7FFF; + oil_scaleconv_f32_s16(b, ca, n, &add, &factor); +} +#endif +} + +void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, void *b) { + int16_t *cb = b; + + assert(a); + assert(b); + +#if SWAP_WORDS == 1 + + for (; n > 0; n--) { + int16_t s; + float v = *(a++); + + if (v > 1) + v = 1; + + if (v < -1) + v = -1; + + s = (int16_t) (v * 0x7FFF); + *(cb++) = INT16_TO(s); + } + +#else +{ + static const double add = 0, factor = 0x7FFF; + oil_scaleconv_s16_f32(cb, a, n, &add, &factor); +} +#endif +} diff --git a/src/polypcore/sconv-s16le.h b/src/polypcore/sconv-s16le.h new file mode 100644 index 00000000..ef5e31e8 --- /dev/null +++ b/src/polypcore/sconv-s16le.h @@ -0,0 +1,28 @@ +#ifndef foosconv_s16lefoo +#define foosconv_s16lefoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +void pa_sconv_s16le_to_float32ne(unsigned n, const void *a, float *b); +void pa_sconv_s16le_from_float32ne(unsigned n, const float *a, void *b); + +#endif diff --git a/src/polypcore/sconv.c b/src/polypcore/sconv.c new file mode 100644 index 00000000..1fcb5a0c --- /dev/null +++ b/src/polypcore/sconv.c @@ -0,0 +1,168 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include + +#include "endianmacros.h" +#include "sconv.h" +#include "g711.h" + +#include "sconv-s16le.h" +#include "sconv-s16be.h" + +static void u8_to_float32ne(unsigned n, const void *a, float *b) { + const uint8_t *ca = a; + static const double add = -128.0/127.0, factor = 1.0/127.0; + + assert(a); + assert(b); + + oil_scaleconv_f32_u8(b, ca, n, &add, &factor); +} + +static void u8_from_float32ne(unsigned n, const float *a, void *b) { + uint8_t *cb = b; + static const double add = 128.0, factor = 127.0; + + assert(a); + assert(b); + + oil_scaleconv_u8_f32(cb, a, n, &add, &factor); +} + +static void float32ne_to_float32ne(unsigned n, const void *a, float *b) { + assert(a); + assert(b); + + oil_memcpy(b, a, sizeof(float) * n); +} + +static void float32ne_from_float32ne(unsigned n, const float *a, void *b) { + assert(a); + assert(b); + + oil_memcpy(b, a, sizeof(float) * n); +} + +static void ulaw_to_float32ne(unsigned n, const void *a, float *b) { + const uint8_t *ca = a; + + assert(a); + assert(b); + + for (; n > 0; n--) + *(b++) = st_ulaw2linear16(*(ca++)) * 1.0F / 0x7FFF; +} + +static void ulaw_from_float32ne(unsigned n, const float *a, void *b) { + uint8_t *cb = b; + + assert(a); + assert(b); + + for (; n > 0; n--) { + float v = *(a++); + + if (v > 1) + v = 1; + + if (v < -1) + v = -1; + + *(cb++) = st_14linear2ulaw((int16_t) (v * 0x1FFF)); + } +} + +static void alaw_to_float32ne(unsigned n, const void *a, float *b) { + const uint8_t *ca = a; + + assert(a); + assert(b); + + for (; n > 0; n--) + *(b++) = st_alaw2linear16(*(ca++)) * 1.0F / 0x7FFF; +} + +static void alaw_from_float32ne(unsigned n, const float *a, void *b) { + uint8_t *cb = b; + + assert(a); + assert(b); + + for (; n > 0; n--) { + float v = *(a++); + + if (v > 1) + v = 1; + + if (v < -1) + v = -1; + + *(cb++) = st_13linear2alaw((int16_t) (v * 0xFFF)); + } +} + +pa_convert_to_float32ne_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) { + switch(f) { + case PA_SAMPLE_U8: + return u8_to_float32ne; + case PA_SAMPLE_S16LE: + return pa_sconv_s16le_to_float32ne; + case PA_SAMPLE_S16BE: + return pa_sconv_s16be_to_float32ne; + case PA_SAMPLE_FLOAT32NE: + return float32ne_to_float32ne; + case PA_SAMPLE_ALAW: + return alaw_to_float32ne; + case PA_SAMPLE_ULAW: + return ulaw_to_float32ne; + default: + return NULL; + } +} + +pa_convert_from_float32ne_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) { + switch(f) { + case PA_SAMPLE_U8: + return u8_from_float32ne; + case PA_SAMPLE_S16LE: + return pa_sconv_s16le_from_float32ne; + case PA_SAMPLE_S16BE: + return pa_sconv_s16be_from_float32ne; + case PA_SAMPLE_FLOAT32NE: + return float32ne_from_float32ne; + case PA_SAMPLE_ALAW: + return alaw_from_float32ne; + case PA_SAMPLE_ULAW: + return ulaw_from_float32ne; + default: + return NULL; + } +} diff --git a/src/polypcore/sconv.h b/src/polypcore/sconv.h new file mode 100644 index 00000000..a0c15c24 --- /dev/null +++ b/src/polypcore/sconv.h @@ -0,0 +1,33 @@ +#ifndef foosconvhfoo +#define foosconvhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +typedef void (*pa_convert_to_float32ne_func_t)(unsigned n, const void *a, float *b); +typedef void (*pa_convert_from_float32ne_func_t)(unsigned n, const float *a, void *b); + +pa_convert_to_float32ne_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f); +pa_convert_from_float32ne_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f); + +#endif diff --git a/src/polypcore/sink-input.c b/src/polypcore/sink-input.c new file mode 100644 index 00000000..f447b8cf --- /dev/null +++ b/src/polypcore/sink-input.c @@ -0,0 +1,368 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "sink-input.h" +#include "sample-util.h" +#include "xmalloc.h" +#include "subscribe.h" +#include "log.h" + +#define CONVERT_BUFFER_LENGTH 4096 + +pa_sink_input* pa_sink_input_new( + pa_sink *s, + const char *driver, + const char *name, + const pa_sample_spec *spec, + const pa_channel_map *map, + int variable_rate, + int resample_method) { + + pa_sink_input *i; + pa_resampler *resampler = NULL; + int r; + char st[256]; + pa_channel_map tmap; + + assert(s); + assert(spec); + assert(s->state == PA_SINK_RUNNING); + + if (pa_idxset_size(s->inputs) >= PA_MAX_INPUTS_PER_SINK) { + pa_log_warn(__FILE__": Failed to create sink input: too many inputs per sink.\n"); + return NULL; + } + + if (resample_method == PA_RESAMPLER_INVALID) + resample_method = s->core->resample_method; + + if (!map) { + pa_channel_map_init_auto(&tmap, spec->channels); + map = &tmap; + } + + if (variable_rate || !pa_sample_spec_equal(spec, &s->sample_spec) || !pa_channel_map_equal(map, &s->channel_map)) + if (!(resampler = pa_resampler_new(spec, map, &s->sample_spec, &s->channel_map, s->core->memblock_stat, resample_method))) + return NULL; + + i = pa_xnew(pa_sink_input, 1); + i->ref = 1; + i->state = PA_SINK_INPUT_RUNNING; + i->name = pa_xstrdup(name); + i->driver = pa_xstrdup(driver); + i->owner = NULL; + i->sink = s; + i->client = NULL; + + i->sample_spec = *spec; + i->channel_map = *map; + + pa_cvolume_reset(&i->volume, spec->channels); + + i->peek = NULL; + i->drop = NULL; + i->kill = NULL; + i->get_latency = NULL; + i->underrun = NULL; + i->userdata = NULL; + + i->playing = 0; + + pa_memchunk_reset(&i->resampled_chunk); + i->resampler = resampler; + + assert(s->core); + r = pa_idxset_put(s->core->sink_inputs, i, &i->index); + assert(r == 0 && i->index != PA_IDXSET_INVALID); + r = pa_idxset_put(s->inputs, i, NULL); + assert(r == 0); + + pa_sample_spec_snprint(st, sizeof(st), spec); + pa_log_info(__FILE__": created %u \"%s\" on %u with sample spec \"%s\"\n", i->index, i->name, s->index, st); + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index); + + return i; +} + +void pa_sink_input_disconnect(pa_sink_input *i) { + assert(i); + assert(i->state != PA_SINK_INPUT_DISCONNECTED); + assert(i->sink); + assert(i->sink->core); + + pa_idxset_remove_by_data(i->sink->core->sink_inputs, i, NULL); + pa_idxset_remove_by_data(i->sink->inputs, i, NULL); + + pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index); + i->sink = NULL; + + i->peek = NULL; + i->drop = NULL; + i->kill = NULL; + i->get_latency = NULL; + i->underrun = NULL; + + i->playing = 0; + i->state = PA_SINK_INPUT_DISCONNECTED; +} + +static void sink_input_free(pa_sink_input* i) { + assert(i); + + if (i->state != PA_SINK_INPUT_DISCONNECTED) + pa_sink_input_disconnect(i); + + pa_log_info(__FILE__": freed %u \"%s\"\n", i->index, i->name); + + if (i->resampled_chunk.memblock) + pa_memblock_unref(i->resampled_chunk.memblock); + + if (i->resampler) + pa_resampler_free(i->resampler); + + pa_xfree(i->name); + pa_xfree(i->driver); + pa_xfree(i); +} + +void pa_sink_input_unref(pa_sink_input *i) { + assert(i); + assert(i->ref >= 1); + + if (!(--i->ref)) + sink_input_free(i); +} + +pa_sink_input* pa_sink_input_ref(pa_sink_input *i) { + assert(i); + assert(i->ref >= 1); + + i->ref++; + return i; +} + +void pa_sink_input_kill(pa_sink_input*i) { + assert(i); + assert(i->ref >= 1); + + if (i->kill) + i->kill(i); +} + +pa_usec_t pa_sink_input_get_latency(pa_sink_input *i) { + pa_usec_t r = 0; + + assert(i); + assert(i->ref >= 1); + + if (i->get_latency) + r += i->get_latency(i); + + if (i->resampled_chunk.memblock) + r += pa_bytes_to_usec(i->resampled_chunk.length, &i->sample_spec); + + return r; +} + +int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume) { + int ret = -1; + int do_volume_adj_here; + + assert(i); + assert(i->ref >= 1); + assert(chunk); + assert(volume); + + pa_sink_input_ref(i); + + if (!i->peek || !i->drop || i->state == PA_SINK_INPUT_CORKED) + goto finish; + + if (!i->resampler) { + do_volume_adj_here = 0; + ret = i->peek(i, chunk); + goto finish; + } + + do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map); + + while (!i->resampled_chunk.memblock) { + pa_memchunk tchunk; + size_t l; + + if ((ret = i->peek(i, &tchunk)) < 0) + goto finish; + + assert(tchunk.length); + + l = pa_resampler_request(i->resampler, CONVERT_BUFFER_LENGTH); + + if (l > tchunk.length) + l = tchunk.length; + + i->drop(i, &tchunk, l); + tchunk.length = l; + + /* It might be necessary to adjust the volume here */ + if (do_volume_adj_here) { + pa_memchunk_make_writable(&tchunk, i->sink->core->memblock_stat, 0); + pa_volume_memchunk(&tchunk, &i->sample_spec, &i->volume); + } + + pa_resampler_run(i->resampler, &tchunk, &i->resampled_chunk); + pa_memblock_unref(tchunk.memblock); + } + + assert(i->resampled_chunk.memblock); + assert(i->resampled_chunk.length); + + *chunk = i->resampled_chunk; + pa_memblock_ref(i->resampled_chunk.memblock); + + ret = 0; + +finish: + + if (ret < 0 && i->playing && i->underrun) + i->underrun(i); + + i->playing = ret >= 0; + + if (ret >= 0) { + /* Let's see if we had to apply the volume adjustment + * ourselves, or if this can be done by the sink for us */ + + if (do_volume_adj_here) + /* We've both the same channel map, so let's have the sink do the adjustment for us*/ + + pa_cvolume_reset(volume, i->sample_spec.channels); + else + /* We had different channel maps, so we already did the adjustment */ + *volume = i->volume; + } + + pa_sink_input_unref(i); + + return ret; +} + +void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) { + assert(i); + assert(i->ref >= 1); + assert(length > 0); + + if (!i->resampler) { + if (i->drop) + i->drop(i, chunk, length); + return; + } + + assert(i->resampled_chunk.memblock); + assert(i->resampled_chunk.length >= length); + + i->resampled_chunk.index += length; + i->resampled_chunk.length -= length; + + if (i->resampled_chunk.length <= 0) { + pa_memblock_unref(i->resampled_chunk.memblock); + i->resampled_chunk.memblock = NULL; + i->resampled_chunk.index = i->resampled_chunk.length = 0; + } +} + +void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { + assert(i); + assert(i->ref >= 1); + assert(i->sink); + assert(i->sink->core); + + if (pa_cvolume_equal(&i->volume, volume)) + return; + + i->volume = *volume; + pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +} + +const pa_cvolume * pa_sink_input_get_volume(pa_sink_input *i) { + assert(i); + assert(i->ref >= 1); + + return &i->volume; +} + +void pa_sink_input_cork(pa_sink_input *i, int b) { + int n; + + assert(i); + assert(i->ref >= 1); + + if (i->state == PA_SINK_INPUT_DISCONNECTED) + return; + + n = i->state == PA_SINK_INPUT_CORKED && !b; + + i->state = b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING; + + if (n) + pa_sink_notify(i->sink); +} + +void pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) { + assert(i); + assert(i->resampler); + assert(i->ref >= 1); + + if (i->sample_spec.rate == rate) + return; + + i->sample_spec.rate = rate; + pa_resampler_set_input_rate(i->resampler, rate); +} + +void pa_sink_input_set_name(pa_sink_input *i, const char *name) { + assert(i); + assert(i->ref >= 1); + + pa_xfree(i->name); + i->name = pa_xstrdup(name); + + pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); +} + +pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) { + assert(i); + assert(i->ref >= 1); + + if (!i->resampler) + return PA_RESAMPLER_INVALID; + + return pa_resampler_get_method(i->resampler); +} diff --git a/src/polypcore/sink-input.h b/src/polypcore/sink-input.h new file mode 100644 index 00000000..a5ad1958 --- /dev/null +++ b/src/polypcore/sink-input.h @@ -0,0 +1,106 @@ +#ifndef foosinkinputhfoo +#define foosinkinputhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +typedef struct pa_sink_input pa_sink_input; + +#include "sink.h" +#include +#include "memblockq.h" +#include "resampler.h" +#include "module.h" +#include "client.h" + +typedef enum pa_sink_input_state { + PA_SINK_INPUT_RUNNING, + PA_SINK_INPUT_CORKED, + PA_SINK_INPUT_DISCONNECTED +} pa_sink_input_state_t; + +struct pa_sink_input { + int ref; + uint32_t index; + pa_sink_input_state_t state; + + char *name, *driver; + pa_module *owner; + + pa_sink *sink; + pa_client *client; + + pa_sample_spec sample_spec; + pa_channel_map channel_map; + + pa_cvolume volume; + + int (*peek) (pa_sink_input *i, pa_memchunk *chunk); + void (*drop) (pa_sink_input *i, const pa_memchunk *chunk, size_t length); + void (*kill) (pa_sink_input *i); + pa_usec_t (*get_latency) (pa_sink_input *i); + void (*underrun) (pa_sink_input *i); + + void *userdata; + + int playing; + + pa_memchunk resampled_chunk; + pa_resampler *resampler; +}; + +pa_sink_input* pa_sink_input_new( + pa_sink *s, + const char *driver, + const char *name, + const pa_sample_spec *spec, + const pa_channel_map *map, + int variable_rate, + int resample_method); + +void pa_sink_input_unref(pa_sink_input* i); +pa_sink_input* pa_sink_input_ref(pa_sink_input* i); + +/* To be called by the implementing module only */ +void pa_sink_input_disconnect(pa_sink_input* i); + +/* External code may request disconnection with this funcion */ +void pa_sink_input_kill(pa_sink_input*i); + +pa_usec_t pa_sink_input_get_latency(pa_sink_input *i); + +int pa_sink_input_peek(pa_sink_input *i, pa_memchunk *chunk, pa_cvolume *volume); +void pa_sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length); + +void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume); +const pa_cvolume * pa_sink_input_get_volume(pa_sink_input *i); + +void pa_sink_input_cork(pa_sink_input *i, int b); + +void pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate); + +void pa_sink_input_set_name(pa_sink_input *i, const char *name); + +pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i); + +#endif diff --git a/src/polypcore/sink.c b/src/polypcore/sink.c new file mode 100644 index 00000000..411befe7 --- /dev/null +++ b/src/polypcore/sink.c @@ -0,0 +1,445 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "sink.h" +#include "sink-input.h" +#include "namereg.h" +#include "util.h" +#include "sample-util.h" +#include "xmalloc.h" +#include "subscribe.h" +#include "log.h" +#include + +#define MAX_MIX_CHANNELS 32 + +pa_sink* pa_sink_new( + pa_core *core, + const char *driver, + const char *name, + int fail, + const pa_sample_spec *spec, + const pa_channel_map *map) { + + pa_sink *s; + char *n = NULL; + char st[256]; + int r; + + assert(core); + assert(name); + assert(*name); + assert(spec); + + s = pa_xnew(pa_sink, 1); + + if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) { + pa_xfree(s); + return NULL; + } + + s->ref = 1; + s->core = core; + s->state = PA_SINK_RUNNING; + s->name = pa_xstrdup(name); + s->description = NULL; + s->driver = pa_xstrdup(driver); + s->owner = NULL; + + s->sample_spec = *spec; + if (map) + s->channel_map = *map; + else + pa_channel_map_init_auto(&s->channel_map, spec->channels); + + s->inputs = pa_idxset_new(NULL, NULL); + + pa_cvolume_reset(&s->sw_volume, spec->channels); + pa_cvolume_reset(&s->hw_volume, spec->channels); + + s->get_latency = NULL; + s->notify = NULL; + s->set_hw_volume = NULL; + s->get_hw_volume = NULL; + s->userdata = NULL; + + r = pa_idxset_put(core->sinks, s, &s->index); + assert(s->index != PA_IDXSET_INVALID && r >= 0); + + pa_sample_spec_snprint(st, sizeof(st), spec); + pa_log_info(__FILE__": created %u \"%s\" with sample spec \"%s\"\n", s->index, s->name, st); + + n = pa_sprintf_malloc("%s_monitor", name); + s->monitor_source = pa_source_new(core, driver, n, 0, spec, map); + assert(s->monitor_source); + pa_xfree(n); + s->monitor_source->monitor_of = s; + s->monitor_source->description = pa_sprintf_malloc("Monitor source of sink '%s'", s->name); + + pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index); + + return s; +} + +void pa_sink_disconnect(pa_sink* s) { + pa_sink_input *i, *j = NULL; + + assert(s); + assert(s->state == PA_SINK_RUNNING); + + pa_namereg_unregister(s->core, s->name); + + while ((i = pa_idxset_first(s->inputs, NULL))) { + assert(i != j); + pa_sink_input_kill(i); + j = i; + } + + pa_source_disconnect(s->monitor_source); + + pa_idxset_remove_by_data(s->core->sinks, s, NULL); + + s->get_latency = NULL; + s->notify = NULL; + s->get_hw_volume = NULL; + s->set_hw_volume = NULL; + + s->state = PA_SINK_DISCONNECTED; + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); +} + +static void sink_free(pa_sink *s) { + assert(s); + assert(!s->ref); + + if (s->state != PA_SINK_DISCONNECTED) + pa_sink_disconnect(s); + + pa_log_info(__FILE__": freed %u \"%s\"\n", s->index, s->name); + + pa_source_unref(s->monitor_source); + s->monitor_source = NULL; + + pa_idxset_free(s->inputs, NULL, NULL); + + pa_xfree(s->name); + pa_xfree(s->description); + pa_xfree(s->driver); + pa_xfree(s); +} + +void pa_sink_unref(pa_sink*s) { + assert(s); + assert(s->ref >= 1); + + if (!(--s->ref)) + sink_free(s); +} + +pa_sink* pa_sink_ref(pa_sink *s) { + assert(s); + assert(s->ref >= 1); + + s->ref++; + return s; +} + +void pa_sink_notify(pa_sink*s) { + assert(s); + assert(s->ref >= 1); + + if (s->notify) + s->notify(s); +} + +static unsigned fill_mix_info(pa_sink *s, pa_mix_info *info, unsigned maxinfo) { + uint32_t idx = PA_IDXSET_INVALID; + pa_sink_input *i; + unsigned n = 0; + + assert(s); + assert(s->ref >= 1); + assert(info); + + for (i = pa_idxset_first(s->inputs, &idx); maxinfo > 0 && i; i = pa_idxset_next(s->inputs, &idx)) { + /* Increase ref counter, to make sure that this input doesn't + * vanish while we still need it */ + pa_sink_input_ref(i); + + if (pa_sink_input_peek(i, &info->chunk, &info->volume) < 0) { + pa_sink_input_unref(i); + continue; + } + + info->userdata = i; + + assert(info->chunk.memblock); + assert(info->chunk.memblock->data); + assert(info->chunk.length); + + info++; + maxinfo--; + n++; + } + + return n; +} + +static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned maxinfo, size_t length) { + assert(s); + assert(s->ref >= 1); + assert(info); + + for (; maxinfo > 0; maxinfo--, info++) { + pa_sink_input *i = info->userdata; + + assert(i); + assert(info->chunk.memblock); + + /* Drop read data */ + pa_sink_input_drop(i, &info->chunk, length); + pa_memblock_unref(info->chunk.memblock); + + /* Decrease ref counter */ + pa_sink_input_unref(i); + info->userdata = NULL; + } +} + +int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { + pa_mix_info info[MAX_MIX_CHANNELS]; + unsigned n; + int r = -1; + + assert(s); + assert(s->ref >= 1); + assert(length); + assert(result); + + pa_sink_ref(s); + + n = fill_mix_info(s, info, MAX_MIX_CHANNELS); + + if (n <= 0) + goto finish; + + if (n == 1) { + pa_cvolume volume; + + *result = info[0].chunk; + pa_memblock_ref(result->memblock); + + if (result->length > length) + result->length = length; + + pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); + + if (!pa_cvolume_is_norm(&volume)) { + pa_memchunk_make_writable(result, s->core->memblock_stat, 0); + pa_volume_memchunk(result, &s->sample_spec, &volume); + } + } else { + result->memblock = pa_memblock_new(length, s->core->memblock_stat); + assert(result->memblock); + + result->length = pa_mix(info, n, result->memblock->data, length, &s->sample_spec, &s->sw_volume); + result->index = 0; + } + + inputs_drop(s, info, n, result->length); + pa_source_post(s->monitor_source, result); + + r = 0; + +finish: + pa_sink_unref(s); + + return r; +} + +int pa_sink_render_into(pa_sink*s, pa_memchunk *target) { + pa_mix_info info[MAX_MIX_CHANNELS]; + unsigned n; + int r = -1; + + assert(s); + assert(s->ref >= 1); + assert(target); + assert(target->memblock); + assert(target->length); + assert(target->memblock->data); + + pa_sink_ref(s); + + n = fill_mix_info(s, info, MAX_MIX_CHANNELS); + + if (n <= 0) + goto finish; + + if (n == 1) { + pa_cvolume volume; + + if (target->length > info[0].chunk.length) + target->length = info[0].chunk.length; + + memcpy((uint8_t*) target->memblock->data + target->index, + (uint8_t*) info[0].chunk.memblock->data + info[0].chunk.index, + target->length); + + pa_sw_cvolume_multiply(&volume, &s->sw_volume, &info[0].volume); + + if (!pa_cvolume_is_norm(&volume)) + pa_volume_memchunk(target, &s->sample_spec, &volume); + } else + target->length = pa_mix(info, n, + (uint8_t*) target->memblock->data + target->index, + target->length, + &s->sample_spec, + &s->sw_volume); + + inputs_drop(s, info, n, target->length); + pa_source_post(s->monitor_source, target); + + r = 0; + +finish: + pa_sink_unref(s); + + return r; +} + +void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { + pa_memchunk chunk; + size_t l, d; + + assert(s); + assert(s->ref >= 1); + assert(target); + assert(target->memblock); + assert(target->length); + assert(target->memblock->data); + + pa_sink_ref(s); + + l = target->length; + d = 0; + while (l > 0) { + chunk = *target; + chunk.index += d; + chunk.length -= d; + + if (pa_sink_render_into(s, &chunk) < 0) + break; + + d += chunk.length; + l -= chunk.length; + } + + if (l > 0) { + chunk = *target; + chunk.index += d; + chunk.length -= d; + pa_silence_memchunk(&chunk, &s->sample_spec); + } + + pa_sink_unref(s); +} + +void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { + assert(s); + assert(s->ref >= 1); + assert(length); + assert(result); + + /*** This needs optimization ***/ + + result->memblock = pa_memblock_new(result->length = length, s->core->memblock_stat); + result->index = 0; + + pa_sink_render_into_full(s, result); +} + +pa_usec_t pa_sink_get_latency(pa_sink *s) { + assert(s); + assert(s->ref >= 1); + + if (!s->get_latency) + return 0; + + return s->get_latency(s); +} + +void pa_sink_set_owner(pa_sink *s, pa_module *m) { + assert(s); + assert(s->ref >= 1); + + s->owner = m; + + if (s->monitor_source) + pa_source_set_owner(s->monitor_source, m); +} + +void pa_sink_set_volume(pa_sink *s, pa_mixer_t m, const pa_cvolume *volume) { + pa_cvolume *v; + + assert(s); + assert(s->ref >= 1); + assert(volume); + + if (m == PA_MIXER_HARDWARE && s->set_hw_volume) + v = &s->hw_volume; + else + v = &s->sw_volume; + + if (pa_cvolume_equal(v, volume)) + return; + + *v = *volume; + + if (v == &s->hw_volume) + if (s->set_hw_volume(s) < 0) + s->sw_volume = *volume; + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +} + +const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_mixer_t m) { + assert(s); + assert(s->ref >= 1); + + if (m == PA_MIXER_HARDWARE && s->set_hw_volume) { + + if (s->get_hw_volume) + s->get_hw_volume(s); + + return &s->hw_volume; + } else + return &s->sw_volume; +} diff --git a/src/polypcore/sink.h b/src/polypcore/sink.h new file mode 100644 index 00000000..268461ef --- /dev/null +++ b/src/polypcore/sink.h @@ -0,0 +1,100 @@ +#ifndef foosinkhfoo +#define foosinkhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +typedef struct pa_sink pa_sink; + +#include "core.h" +#include +#include "idxset.h" +#include "source.h" +#include +#include "module.h" +#include + +#define PA_MAX_INPUTS_PER_SINK 6 + +typedef enum pa_sink_state { + PA_SINK_RUNNING, + PA_SINK_DISCONNECTED +} pa_sink_state_t; + +typedef enum pa_mixer { + PA_MIXER_SOFTWARE, + PA_MIXER_HARDWARE +} pa_mixer_t; + +struct pa_sink { + int ref; + uint32_t index; + pa_core *core; + pa_sink_state_t state; + + char *name, *description, *driver; + pa_module *owner; + + pa_sample_spec sample_spec; + pa_channel_map channel_map; + + pa_idxset *inputs; + pa_source *monitor_source; + + pa_cvolume hw_volume, sw_volume; + + void (*notify)(pa_sink*sink); + pa_usec_t (*get_latency)(pa_sink *s); + int (*set_hw_volume)(pa_sink *s); + int (*get_hw_volume)(pa_sink *s); + + void *userdata; +}; + +pa_sink* pa_sink_new( + pa_core *core, + const char *driver, + const char *name, + int namereg_fail, + const pa_sample_spec *spec, + const pa_channel_map *map); + +void pa_sink_disconnect(pa_sink* s); +void pa_sink_unref(pa_sink*s); +pa_sink* pa_sink_ref(pa_sink *s); + +int pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result); +void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result); +int pa_sink_render_into(pa_sink*s, pa_memchunk *target); +void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target); + +pa_usec_t pa_sink_get_latency(pa_sink *s); + +void pa_sink_notify(pa_sink*s); + +void pa_sink_set_owner(pa_sink *sink, pa_module *m); + +void pa_sink_set_volume(pa_sink *sink, pa_mixer_t m, const pa_cvolume *volume); +const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_mixer_t m); + +#endif diff --git a/src/polypcore/sioman.c b/src/polypcore/sioman.c new file mode 100644 index 00000000..8d7b136c --- /dev/null +++ b/src/polypcore/sioman.c @@ -0,0 +1,42 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include "sioman.h" + +static int stdio_inuse = 0; + +int pa_stdio_acquire(void) { + if (stdio_inuse) + return -1; + + stdio_inuse = 1; + return 0; +} + +void pa_stdio_release(void) { + assert(stdio_inuse); + stdio_inuse = 0; +} diff --git a/src/polypcore/sioman.h b/src/polypcore/sioman.h new file mode 100644 index 00000000..840d93f2 --- /dev/null +++ b/src/polypcore/sioman.h @@ -0,0 +1,28 @@ +#ifndef foosiomanhfoo +#define foosiomanhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +int pa_stdio_acquire(void); +void pa_stdio_release(void); + +#endif diff --git a/src/polypcore/socket-client.c b/src/polypcore/socket-client.c new file mode 100644 index 00000000..29c9775e --- /dev/null +++ b/src/polypcore/socket-client.c @@ -0,0 +1,513 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* #undef HAVE_LIBASYNCNS */ + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_SYS_UN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif + +#ifdef HAVE_LIBASYNCNS +#include +#endif + +#include "winsock.h" + +#include "socket-client.h" +#include "socket-util.h" +#include "util.h" +#include "xmalloc.h" +#include "log.h" +#include "parseaddr.h" + +#define CONNECT_TIMEOUT 5 + +struct pa_socket_client { + int ref; + pa_mainloop_api *mainloop; + int fd; + pa_io_event *io_event; + pa_time_event *timeout_event; + pa_defer_event *defer_event; + void (*callback)(pa_socket_client*c, pa_iochannel *io, void *userdata); + void *userdata; + int local; +#ifdef HAVE_LIBASYNCNS + asyncns_t *asyncns; + asyncns_query_t * asyncns_query; + pa_io_event *asyncns_io_event; +#endif +}; + +static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) { + pa_socket_client *c; + assert(m); + + c = pa_xmalloc(sizeof(pa_socket_client)); + c->ref = 1; + c->mainloop = m; + c->fd = -1; + c->io_event = NULL; + c->defer_event = NULL; + c->timeout_event = NULL; + c->callback = NULL; + c->userdata = NULL; + c->local = 0; + +#ifdef HAVE_LIBASYNCNS + c->asyncns = NULL; + c->asyncns_io_event = NULL; + c->asyncns_query = NULL; +#endif + + return c; +} + +static void free_events(pa_socket_client *c) { + assert(c); + + if (c->io_event) { + c->mainloop->io_free(c->io_event); + c->io_event = NULL; + } + + if (c->defer_event) { + c->mainloop->defer_free(c->defer_event); + c->defer_event = NULL; + } + + if (c->timeout_event) { + c->mainloop->time_free(c->timeout_event); + c->timeout_event = NULL; + } +} + +static void do_call(pa_socket_client *c) { + pa_iochannel *io = NULL; + int error; + socklen_t lerror; + assert(c && c->callback); + + pa_socket_client_ref(c); + + if (c->fd < 0) + goto finish; + + lerror = sizeof(error); + if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void*)&error, &lerror) < 0) { + pa_log(__FILE__": getsockopt(): %s\n", strerror(errno)); + goto finish; + } + + if (lerror != sizeof(error)) { + pa_log(__FILE__": getsockopt() returned invalid size.\n"); + goto finish; + } + + if (error != 0) { + pa_log_debug(__FILE__": connect(): %s\n", strerror(error)); + errno = error; + goto finish; + } + + io = pa_iochannel_new(c->mainloop, c->fd, c->fd); + assert(io); + +finish: + if (!io && c->fd >= 0) + close(c->fd); + c->fd = -1; + + free_events(c); + + assert(c->callback); + c->callback(c, io, c->userdata); + + pa_socket_client_unref(c); +} + +static void connect_fixed_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { + pa_socket_client *c = userdata; + assert(m && c && c->defer_event == e); + do_call(c); +} + +static void connect_io_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { + pa_socket_client *c = userdata; + assert(m && c && c->io_event == e && fd >= 0); + do_call(c); +} + +static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t len) { + int r; + assert(c && sa && len); + + pa_make_nonblock_fd(c->fd); + + if ((r = connect(c->fd, sa, len)) < 0) { + if (errno != EINPROGRESS) { + /*pa_log(__FILE__": connect(): %s\n", strerror(errno));*/ + return -1; + } + + c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c); + assert(c->io_event); + } else { + c->defer_event = c->mainloop->defer_new(c->mainloop, connect_fixed_cb, c); + assert(c->defer_event); + } + + return 0; +} + +pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port) { + struct sockaddr_in sa; + assert(m && port > 0); + + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + sa.sin_addr.s_addr = htonl(address); + + return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa)); +} + +#ifdef HAVE_SYS_UN_H + +pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename) { + struct sockaddr_un sa; + assert(m && filename); + + memset(&sa, 0, sizeof(sa)); + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1); + sa.sun_path[sizeof(sa.sun_path) - 1] = 0; + + return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa)); +} + +#else /* HAVE_SYS_UN_H */ + +pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename) { + return NULL; +} + +#endif /* HAVE_SYS_UN_H */ + +static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size_t salen) { + assert(c); + assert(sa); + assert(salen); + + switch (sa->sa_family) { + case AF_UNIX: + c->local = 1; + break; + + case AF_INET: + c->local = ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK; + break; + + case AF_INET6: + c->local = memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0; + break; + + default: + c->local = 0; + } + + if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) { + pa_log(__FILE__": socket(): %s\n", strerror(errno)); + return -1; + } + + pa_fd_set_cloexec(c->fd, 1); + if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6) + pa_socket_tcp_low_delay(c->fd); + else + pa_socket_low_delay(c->fd); + + if (do_connect(c, sa, salen) < 0) + return -1; + + return 0; +} + +pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen) { + pa_socket_client *c; + assert(m && sa); + c = pa_socket_client_new(m); + assert(c); + + if (sockaddr_prepare(c, sa, salen) < 0) + goto fail; + + return c; + +fail: + pa_socket_client_unref(c); + return NULL; + +} + +static void socket_client_free(pa_socket_client *c) { + assert(c && c->mainloop); + + + free_events(c); + + if (c->fd >= 0) + close(c->fd); + +#ifdef HAVE_LIBASYNCNS + if (c->asyncns_query) + asyncns_cancel(c->asyncns, c->asyncns_query); + if (c->asyncns) + asyncns_free(c->asyncns); + if (c->asyncns_io_event) + c->mainloop->io_free(c->asyncns_io_event); +#endif + + pa_xfree(c); +} + +void pa_socket_client_unref(pa_socket_client *c) { + assert(c && c->ref >= 1); + + if (!(--(c->ref))) + socket_client_free(c); +} + +pa_socket_client* pa_socket_client_ref(pa_socket_client *c) { + assert(c && c->ref >= 1); + c->ref++; + return c; +} + +void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa_socket_client *c, pa_iochannel*io, void *userdata), void *userdata) { + assert(c); + c->callback = on_connection; + c->userdata = userdata; +} + +pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port) { + struct sockaddr_in6 sa; + + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; + sa.sin6_port = htons(port); + memcpy(&sa.sin6_addr, address, sizeof(sa.sin6_addr)); + + return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa)); +} + +#ifdef HAVE_LIBASYNCNS + +static void asyncns_cb(pa_mainloop_api*m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { + pa_socket_client *c = userdata; + struct addrinfo *res = NULL; + int ret; + assert(m && c && c->asyncns_io_event == e && fd >= 0); + + if (asyncns_wait(c->asyncns, 0) < 0) + goto fail; + + if (!asyncns_isdone(c->asyncns, c->asyncns_query)) + return; + + ret = asyncns_getaddrinfo_done(c->asyncns, c->asyncns_query, &res); + c->asyncns_query = NULL; + + if (ret != 0 || !res) + goto fail; + + if (res->ai_addr) + sockaddr_prepare(c, res->ai_addr, res->ai_addrlen); + + asyncns_freeaddrinfo(res); + + goto finish; + +fail: + errno == EHOSTUNREACH; + do_call(c); + +finish: + + m->io_free(c->asyncns_io_event); + c->asyncns_io_event = NULL; +} + +#endif + +static void timeout_cb(pa_mainloop_api *m, pa_time_event *e, const struct timeval *tv, void *userdata) { + pa_socket_client *c = userdata; + assert(m); + assert(e); + assert(tv); + assert(c); + + if (c->fd >= 0) { + close(c->fd); + c->fd = -1; + } + + errno = ETIMEDOUT; + do_call(c); +} + +static void start_timeout(pa_socket_client *c) { + struct timeval tv; + assert(c); + assert(!c->timeout_event); + + pa_gettimeofday(&tv); + pa_timeval_add(&tv, CONNECT_TIMEOUT * 1000000); + c->timeout_event = c->mainloop->time_new(c->mainloop, &tv, timeout_cb, c); +} + +pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*name, uint16_t default_port) { + pa_socket_client *c = NULL; + pa_parsed_address a; + assert(m && name); + + if (pa_parse_address(name, &a) < 0) + return NULL; + + if (!a.port) + a.port = default_port; + + switch (a.type) { + case PA_PARSED_ADDRESS_UNIX: + if ((c = pa_socket_client_new_unix(m, a.path_or_host))) + start_timeout(c); + break; + + case PA_PARSED_ADDRESS_TCP4: /* Fallthrough */ + case PA_PARSED_ADDRESS_TCP6: /* Fallthrough */ + case PA_PARSED_ADDRESS_TCP_AUTO:{ + + struct addrinfo hints; + char port[12]; + + snprintf(port, sizeof(port), "%u", (unsigned) a.port); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = a.type == PA_PARSED_ADDRESS_TCP4 ? PF_INET : (a.type == PA_PARSED_ADDRESS_TCP6 ? PF_INET6 : PF_UNSPEC); + hints.ai_socktype = SOCK_STREAM; + +#ifdef HAVE_LIBASYNCNS + { + asyncns_t *asyncns; + + if (!(asyncns = asyncns_new(1))) + goto finish; + + c = pa_socket_client_new(m); + c->asyncns = asyncns; + c->asyncns_io_event = m->io_new(m, asyncns_fd(c->asyncns), PA_IO_EVENT_INPUT, asyncns_cb, c); + c->asyncns_query = asyncns_getaddrinfo(c->asyncns, a.path_or_host, port, &hints); + assert(c->asyncns_query); + start_timeout(c); + } +#else /* HAVE_LIBASYNCNS */ + { +#ifdef HAVE_GETADDRINFO + int ret; + struct addrinfo *res = NULL; + + ret = getaddrinfo(a.path_or_host, port, &hints, &res); + + if (ret < 0 || !res) + goto finish; + + if (res->ai_addr) { + if ((c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen))) + start_timeout(c); + } + + freeaddrinfo(res); +#else /* HAVE_GETADDRINFO */ + struct hostent *host = NULL; + struct sockaddr_in s; + + /* FIXME: PF_INET6 support */ + if (hints.ai_family != PF_INET) + goto finish; + + host = gethostbyname(a.path_or_host); + if (!host) { + unsigned int addr = inet_addr(a.path_or_host); + if (addr != INADDR_NONE) + host = gethostbyaddr((char*)&addr, 4, AF_INET); + } + + if (!host) + goto finish; + + s.sin_family = AF_INET; + memcpy(&s.sin_addr, host->h_addr, sizeof(struct in_addr)); + s.sin_port = port; + + if ((c = pa_socket_client_new_sockaddr(m, &s, sizeof(s)))) + start_timeout(c); +#endif /* HAVE_GETADDRINFO */ + } +#endif /* HAVE_LIBASYNCNS */ + } + } + +finish: + pa_xfree(a.path_or_host); + return c; + +} + +/* Return non-zero when the target sockaddr is considered + local. "local" means UNIX socket or TCP socket on localhost. Other + local IP addresses are not considered local. */ +int pa_socket_client_is_local(pa_socket_client *c) { + assert(c); + return c->local; +} diff --git a/src/polypcore/socket-client.h b/src/polypcore/socket-client.h new file mode 100644 index 00000000..624880d7 --- /dev/null +++ b/src/polypcore/socket-client.h @@ -0,0 +1,47 @@ +#ifndef foosocketclienthfoo +#define foosocketclienthfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include "iochannel.h" + +struct sockaddr; + +typedef struct pa_socket_client pa_socket_client; + +pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port); +pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port); +pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename); +pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen); +pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char *a, uint16_t default_port); + +void pa_socket_client_unref(pa_socket_client *c); +pa_socket_client* pa_socket_client_ref(pa_socket_client *c); + +void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa_socket_client *c, pa_iochannel*io, void *userdata), void *userdata); + +int pa_socket_client_is_local(pa_socket_client *c); + +#endif diff --git a/src/polypcore/socket-server.c b/src/polypcore/socket-server.c new file mode 100644 index 00000000..262b32a7 --- /dev/null +++ b/src/polypcore/socket-server.c @@ -0,0 +1,432 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_SYS_UN_H +#include +#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 +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_LIBWRAP +#include +#endif + +#ifndef HAVE_INET_NTOP +#include "inet_ntop.h" +#endif + +#include "winsock.h" + +#include "socket-server.h" +#include "socket-util.h" +#include "xmalloc.h" +#include "util.h" +#include "log.h" + +struct pa_socket_server { + int ref; + int fd; + char *filename; + char *tcpwrap_service; + + void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata); + void *userdata; + + pa_io_event *io_event; + pa_mainloop_api *mainloop; + enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX, SOCKET_SERVER_IPV6 } type; +}; + +static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { + pa_socket_server *s = userdata; + pa_iochannel *io; + int nfd; + assert(s && s->mainloop == mainloop && s->io_event == e && e && fd >= 0 && fd == s->fd); + + pa_socket_server_ref(s); + + if ((nfd = accept(fd, NULL, NULL)) < 0) { + pa_log(__FILE__": accept(): %s\n", strerror(errno)); + goto finish; + } + + pa_fd_set_cloexec(nfd, 1); + + if (!s->on_connection) { + close(nfd); + goto finish; + } + +#ifdef HAVE_LIBWRAP + + if (s->type == SOCKET_SERVER_IPV4 && s->tcpwrap_service) { + struct request_info req; + + request_init(&req, RQ_DAEMON, s->tcpwrap_service, RQ_FILE, nfd, NULL); + fromhost(&req); + if (!hosts_access(&req)) { + pa_log(__FILE__": TCP connection refused by tcpwrap.\n"); + close(nfd); + goto finish; + } + + pa_log(__FILE__": TCP connection accepted by tcpwrap.\n"); + } +#endif + + /* There should be a check for socket type here */ + if (s->type == SOCKET_SERVER_IPV4) + pa_socket_tcp_low_delay(fd); + else + pa_socket_low_delay(fd); + + io = pa_iochannel_new(s->mainloop, nfd, nfd); + assert(io); + s->on_connection(s, io, s->userdata); + +finish: + pa_socket_server_unref(s); +} + +pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd) { + pa_socket_server *s; + assert(m && fd >= 0); + + s = pa_xmalloc(sizeof(pa_socket_server)); + s->ref = 1; + s->fd = fd; + s->filename = NULL; + s->on_connection = NULL; + s->userdata = NULL; + s->tcpwrap_service = NULL; + + s->mainloop = m; + s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s); + assert(s->io_event); + + s->type = SOCKET_SERVER_GENERIC; + + return s; +} + +pa_socket_server* pa_socket_server_ref(pa_socket_server *s) { + assert(s && s->ref >= 1); + s->ref++; + return s; +} + +#ifdef HAVE_SYS_UN_H + +pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) { + int fd = -1; + struct sockaddr_un sa; + pa_socket_server *s; + + assert(m && filename); + + if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { + pa_log(__FILE__": socket(): %s\n", strerror(errno)); + goto fail; + } + + pa_fd_set_cloexec(fd, 1); + + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1); + sa.sun_path[sizeof(sa.sun_path) - 1] = 0; + + pa_socket_low_delay(fd); + + if (bind(fd, (struct sockaddr*) &sa, SUN_LEN(&sa)) < 0) { + pa_log(__FILE__": bind(): %s\n", strerror(errno)); + goto fail; + } + + if (listen(fd, 5) < 0) { + pa_log(__FILE__": listen(): %s\n", strerror(errno)); + goto fail; + } + + s = pa_socket_server_new(m, fd); + assert(s); + + s->filename = pa_xstrdup(filename); + s->type = SOCKET_SERVER_UNIX; + + return s; + +fail: + if (fd >= 0) + close(fd); + + return NULL; +} + +#else /* HAVE_SYS_UN_H */ + +pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) { + return NULL; +} + +#endif /* HAVE_SYS_UN_H */ + +pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port, const char *tcpwrap_service) { + pa_socket_server *ss; + int fd = -1; + struct sockaddr_in sa; + int on = 1; + + assert(m && port); + + if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + pa_log(__FILE__": socket(): %s\n", strerror(errno)); + goto fail; + } + + pa_fd_set_cloexec(fd, 1); + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0) + pa_log(__FILE__": setsockopt(): %s\n", strerror(errno)); + + pa_socket_tcp_low_delay(fd); + + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + sa.sin_addr.s_addr = htonl(address); + + if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { + pa_log(__FILE__": bind(): %s\n", strerror(errno)); + goto fail; + } + + if (listen(fd, 5) < 0) { + pa_log(__FILE__": listen(): %s\n", strerror(errno)); + goto fail; + } + + if ((ss = pa_socket_server_new(m, fd))) { + ss->type = SOCKET_SERVER_IPV4; + ss->tcpwrap_service = pa_xstrdup(tcpwrap_service); + } + + return ss; + +fail: + if (fd >= 0) + close(fd); + + return NULL; +} + +pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port) { + pa_socket_server *ss; + int fd = -1; + struct sockaddr_in6 sa; + int on = 1; + + assert(m && port); + + if ((fd = socket(PF_INET6, SOCK_STREAM, 0)) < 0) { + pa_log(__FILE__": socket(): %s\n", strerror(errno)); + goto fail; + } + + pa_fd_set_cloexec(fd, 1); + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0) + pa_log(__FILE__": setsockopt(): %s\n", strerror(errno)); + + pa_socket_tcp_low_delay(fd); + + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; + sa.sin6_port = htons(port); + memcpy(sa.sin6_addr.s6_addr, address, 16); + + if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { + pa_log(__FILE__": bind(): %s\n", strerror(errno)); + goto fail; + } + + if (listen(fd, 5) < 0) { + pa_log(__FILE__": listen(): %s\n", strerror(errno)); + goto fail; + } + + if ((ss = pa_socket_server_new(m, fd))) + ss->type = SOCKET_SERVER_IPV6; + + return ss; + +fail: + if (fd >= 0) + close(fd); + + return NULL; +} + +static void socket_server_free(pa_socket_server*s) { + assert(s); + close(s->fd); + + if (s->filename) { + unlink(s->filename); + pa_xfree(s->filename); + } + + pa_xfree(s->tcpwrap_service); + + s->mainloop->io_free(s->io_event); + pa_xfree(s); +} + +void pa_socket_server_unref(pa_socket_server *s) { + assert(s && s->ref >= 1); + + if (!(--(s->ref))) + socket_server_free(s); +} + +void pa_socket_server_set_callback(pa_socket_server*s, void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata), void *userdata) { + assert(s && s->ref >= 1); + + s->on_connection = on_connection; + s->userdata = userdata; +} + + +char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) { + assert(s && c && l > 0); + + switch (s->type) { + case SOCKET_SERVER_IPV6: { + struct sockaddr_in6 sa; + socklen_t sa_len = sizeof(sa); + + if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) { + pa_log(__FILE__": getsockname() failed: %s\n", strerror(errno)); + return NULL; + } + + if (memcmp(&in6addr_any, &sa.sin6_addr, sizeof(in6addr_any)) == 0) { + char fqdn[256]; + if (!pa_get_fqdn(fqdn, sizeof(fqdn))) + return NULL; + + snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port)); + + } else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) { + char hn[256]; + if (!pa_get_host_name(hn, sizeof(hn))) + return NULL; + + snprintf(c, l, "{%s}tcp6:localhost:%u", hn, (unsigned) ntohs(sa.sin6_port)); + } else { + char ip[INET6_ADDRSTRLEN]; + + if (!inet_ntop(AF_INET6, &sa.sin6_addr, ip, sizeof(ip))) { + pa_log(__FILE__": inet_ntop() failed: %s\n", strerror(errno)); + return NULL; + } + + snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port)); + } + + return c; + } + + case SOCKET_SERVER_IPV4: { + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + + if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) { + pa_log(__FILE__": getsockname() failed: %s\n", strerror(errno)); + return NULL; + } + + if (sa.sin_addr.s_addr == INADDR_ANY) { + char fqdn[256]; + if (!pa_get_fqdn(fqdn, sizeof(fqdn))) + return NULL; + + snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port)); + } else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) { + char hn[256]; + if (!pa_get_host_name(hn, sizeof(hn))) + return NULL; + + snprintf(c, l, "{%s}tcp:localhost:%u", hn, (unsigned) ntohs(sa.sin_port)); + } else { + char ip[INET_ADDRSTRLEN]; + + if (!inet_ntop(AF_INET, &sa.sin_addr, ip, sizeof(ip))) { + pa_log(__FILE__": inet_ntop() failed: %s\n", strerror(errno)); + return NULL; + } + + snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port)); + + } + + return c; + } + + case SOCKET_SERVER_UNIX: { + char hn[256]; + + if (!s->filename) + return NULL; + + if (!pa_get_host_name(hn, sizeof(hn))) + return NULL; + + snprintf(c, l, "{%s}unix:%s", hn, s->filename); + return c; + } + + default: + return NULL; + } +} diff --git a/src/polypcore/socket-server.h b/src/polypcore/socket-server.h new file mode 100644 index 00000000..4d8bfad5 --- /dev/null +++ b/src/polypcore/socket-server.h @@ -0,0 +1,45 @@ +#ifndef foosocketserverhfoo +#define foosocketserverhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include "iochannel.h" + +/* It is safe to destroy the calling socket_server object from the callback */ + +typedef struct pa_socket_server pa_socket_server; + +pa_socket_server* pa_socket_server_new(pa_mainloop_api *m, int fd); +pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename); +pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port, const char *tcpwrap_service); +pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port); + +void pa_socket_server_unref(pa_socket_server*s); +pa_socket_server* pa_socket_server_ref(pa_socket_server *s); + +void pa_socket_server_set_callback(pa_socket_server*s, void (*on_connection)(pa_socket_server*s, pa_iochannel *io, void *userdata), void *userdata); + +char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l); + +#endif diff --git a/src/polypcore/socket-util.c b/src/polypcore/socket-util.c new file mode 100644 index 00000000..032a3c91 --- /dev/null +++ b/src/polypcore/socket-util.c @@ -0,0 +1,246 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_SYS_UN_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETINET_IN_SYSTM_H +#include +#endif +#ifdef HAVE_NETINET_IP_H +#include +#endif +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif + +#include "winsock.h" + +#include "socket-util.h" +#include "util.h" +#include "xmalloc.h" +#include "log.h" + +void pa_socket_peer_to_string(int fd, char *c, size_t l) { + struct stat st; + + assert(c && l && fd >= 0); + +#ifndef OS_IS_WIN32 + if (fstat(fd, &st) < 0) { + snprintf(c, l, "Invalid client fd"); + return; + } +#endif + +#ifndef OS_IS_WIN32 + if (S_ISSOCK(st.st_mode)) { +#endif + union { + struct sockaddr sa; + struct sockaddr_in in; +#ifdef HAVE_SYS_UN_H + struct sockaddr_un un; +#endif + } sa; + socklen_t sa_len = sizeof(sa); + + if (getpeername(fd, &sa.sa, &sa_len) >= 0) { + + if (sa.sa.sa_family == AF_INET) { + uint32_t ip = ntohl(sa.in.sin_addr.s_addr); + + snprintf(c, l, "TCP/IP client from %i.%i.%i.%i:%u", + ip >> 24, + (ip >> 16) & 0xFF, + (ip >> 8) & 0xFF, + ip & 0xFF, + ntohs(sa.in.sin_port)); + return; +#ifdef HAVE_SYS_UN_H + } else if (sa.sa.sa_family == AF_UNIX) { + snprintf(c, l, "UNIX socket client"); + return; +#endif + } + + } +#ifndef OS_IS_WIN32 + snprintf(c, l, "Unknown network client"); + return; + } else if (S_ISCHR(st.st_mode) && (fd == 0 || fd == 1)) { + snprintf(c, l, "STDIN/STDOUT client"); + return; + } +#endif /* OS_IS_WIN32 */ + + snprintf(c, l, "Unknown client"); +} + +int pa_socket_low_delay(int fd) { +#ifdef SO_PRIORITY + int priority; + assert(fd >= 0); + + priority = 7; + if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (void*)&priority, sizeof(priority)) < 0) + return -1; +#endif + + return 0; +} + +int pa_socket_tcp_low_delay(int fd) { + int ret, tos, on; + + assert(fd >= 0); + + ret = pa_socket_low_delay(fd); + + on = 1; + tos = 0; + +#if defined(SOL_TCP) || defined(IPPROTO_TCP) +#if defined(SOL_TCP) + if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0) +#else + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on)) < 0) +#endif + ret = -1; +#endif + +#if defined(IPTOS_LOWDELAY) && defined(IP_TOS) && (defined(SOL_IP) || \ + defined(IPPROTO_IP)) + tos = IPTOS_LOWDELAY; +#ifdef SOL_IP + if (setsockopt(fd, SOL_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0) +#else + if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) < 0) +#endif + ret = -1; +#endif + + return ret; + +} + +int pa_socket_set_rcvbuf(int fd, size_t l) { + assert(fd >= 0); + +/* if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&l, sizeof(l)) < 0) { */ +/* pa_log(__FILE__": SO_RCVBUF: %s\n", strerror(errno)); */ +/* return -1; */ +/* } */ + + return 0; +} + +int pa_socket_set_sndbuf(int fd, size_t l) { + assert(fd >= 0); + +/* if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&l, sizeof(l)) < 0) { */ +/* pa_log(__FILE__": SO_SNDBUF: %s\n", strerror(errno)); */ +/* return -1; */ +/* } */ + + return 0; +} + +#ifdef HAVE_SYS_UN_H + +int pa_unix_socket_is_stale(const char *fn) { + struct sockaddr_un sa; + int fd = -1, ret = -1; + + if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { + pa_log(__FILE__": socket(): %s\n", strerror(errno)); + goto finish; + } + + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, fn, sizeof(sa.sun_path)-1); + sa.sun_path[sizeof(sa.sun_path) - 1] = 0; + + if (connect(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) { + if (errno == ECONNREFUSED) + ret = 1; + } else + ret = 0; + +finish: + if (fd >= 0) + close(fd); + + return ret; +} + +int pa_unix_socket_remove_stale(const char *fn) { + int r; + + if ((r = pa_unix_socket_is_stale(fn)) < 0) + return errno != ENOENT ? -1 : 0; + + if (!r) + return 0; + + /* Yes, here is a race condition. But who cares? */ + if (unlink(fn) < 0) + return -1; + + return 0; +} + +#else /* HAVE_SYS_UN_H */ + +int pa_unix_socket_is_stale(const char *fn) { + return -1; +} + +int pa_unix_socket_remove_stale(const char *fn) { + return -1; +} + +#endif /* HAVE_SYS_UN_H */ diff --git a/src/polypcore/socket-util.h b/src/polypcore/socket-util.h new file mode 100644 index 00000000..ae16fb16 --- /dev/null +++ b/src/polypcore/socket-util.h @@ -0,0 +1,38 @@ +#ifndef foosocketutilhfoo +#define foosocketutilhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +void pa_socket_peer_to_string(int fd, char *c, size_t l); + +int pa_socket_low_delay(int fd); +int pa_socket_tcp_low_delay(int fd); + +int pa_socket_set_sndbuf(int fd, size_t l); +int pa_socket_set_rcvbuf(int fd, size_t l); + +int pa_unix_socket_is_stale(const char *fn); +int pa_unix_socket_remove_stale(const char *fn); + +#endif diff --git a/src/polypcore/sound-file-stream.c b/src/polypcore/sound-file-stream.c new file mode 100644 index 00000000..881e3077 --- /dev/null +++ b/src/polypcore/sound-file-stream.c @@ -0,0 +1,179 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include + +#include "sound-file-stream.h" +#include "sink-input.h" +#include "xmalloc.h" +#include "log.h" + +#define BUF_SIZE (1024*10) + +struct userdata { + SNDFILE *sndfile; + pa_sink_input *sink_input; + pa_memchunk memchunk; + sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames); +}; + +static void free_userdata(struct userdata *u) { + assert(u); + if (u->sink_input) { + pa_sink_input_disconnect(u->sink_input); + pa_sink_input_unref(u->sink_input); + } + + if (u->memchunk.memblock) + pa_memblock_unref(u->memchunk.memblock); + if (u->sndfile) + sf_close(u->sndfile); + + pa_xfree(u); +} + +static void sink_input_kill(pa_sink_input *i) { + assert(i && i->userdata); + free_userdata(i->userdata); +} + +static void si_kill(PA_GCC_UNUSED pa_mainloop_api *m, void *i) { + sink_input_kill(i); +} + +static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) { + struct userdata *u; + assert(i && chunk && i->userdata); + u = i->userdata; + + if (!u->memchunk.memblock) { + uint32_t fs = pa_frame_size(&i->sample_spec); + sf_count_t samples = BUF_SIZE/fs; + + u->memchunk.memblock = pa_memblock_new(BUF_SIZE, i->sink->core->memblock_stat); + u->memchunk.index = 0; + samples = u->readf_function(u->sndfile, u->memchunk.memblock->data, samples); + u->memchunk.length = samples*fs; + + if (!u->memchunk.length) { + pa_memblock_unref(u->memchunk.memblock); + u->memchunk.memblock = NULL; + u->memchunk.index = u->memchunk.length = 0; + pa_mainloop_api_once(i->sink->core->mainloop, si_kill, i); + return -1; + } + } + + *chunk = u->memchunk; + pa_memblock_ref(chunk->memblock); + assert(chunk->length); + return 0; +} + +static void sink_input_drop(pa_sink_input *i, const pa_memchunk*chunk, size_t length) { + struct userdata *u; + assert(i && chunk && length && i->userdata); + u = i->userdata; + + assert(!memcmp(chunk, &u->memchunk, sizeof(chunk))); + assert(length <= u->memchunk.length); + + u->memchunk.index += length; + u->memchunk.length -= length; + + if (u->memchunk.length <= 0) { + pa_memblock_unref(u->memchunk.memblock); + u->memchunk.memblock = NULL; + u->memchunk.index = u->memchunk.length = 0; + } +} + +int pa_play_file(pa_sink *sink, const char *fname, const pa_cvolume *volume) { + struct userdata *u = NULL; + SF_INFO sfinfo; + pa_sample_spec ss; + assert(sink && fname); + + u = pa_xmalloc(sizeof(struct userdata)); + u->sink_input = NULL; + u->memchunk.memblock = NULL; + u->memchunk.index = u->memchunk.length = 0; + u->sndfile = NULL; + + memset(&sfinfo, 0, sizeof(sfinfo)); + + if (!(u->sndfile = sf_open(fname, SFM_READ, &sfinfo))) { + pa_log(__FILE__": Failed to open file %s\n", fname); + goto fail; + } + + switch (sfinfo.format & 0xFF) { + case SF_FORMAT_PCM_16: + case SF_FORMAT_PCM_U8: + case SF_FORMAT_ULAW: + case SF_FORMAT_ALAW: + ss.format = PA_SAMPLE_S16NE; + u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short; + break; + case SF_FORMAT_FLOAT: + default: + ss.format = PA_SAMPLE_FLOAT32NE; + u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float; + break; + } + + ss.rate = sfinfo.samplerate; + ss.channels = sfinfo.channels; + + if (!pa_sample_spec_valid(&ss)) { + pa_log(__FILE__": Unsupported sample format in file %s\n", fname); + goto fail; + } + + if (!(u->sink_input = pa_sink_input_new(sink, __FILE__, fname, &ss, NULL, 0, -1))) + goto fail; + + if (volume) + u->sink_input->volume = *volume; + u->sink_input->peek = sink_input_peek; + u->sink_input->drop = sink_input_drop; + u->sink_input->kill = sink_input_kill; + u->sink_input->userdata = u; + + pa_sink_notify(sink); + + return 0; + +fail: + if (u) + free_userdata(u); + + return -1; +} diff --git a/src/polypcore/sound-file-stream.h b/src/polypcore/sound-file-stream.h new file mode 100644 index 00000000..2e56ef49 --- /dev/null +++ b/src/polypcore/sound-file-stream.h @@ -0,0 +1,29 @@ +#ifndef foosoundfilestreamhfoo +#define foosoundfilestreamhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "sink.h" + +int pa_play_file(pa_sink *sink, const char *fname, const pa_cvolume *volume); + +#endif diff --git a/src/polypcore/sound-file.c b/src/polypcore/sound-file.c new file mode 100644 index 00000000..a81c283a --- /dev/null +++ b/src/polypcore/sound-file.c @@ -0,0 +1,139 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +#include "sound-file.h" +#include +#include "log.h" + +#define MAX_FILE_SIZE (1024*1024) + +int pa_sound_file_load(const char *fname, pa_sample_spec *ss, pa_memchunk *chunk, pa_memblock_stat *s) { + SNDFILE*sf = NULL; + SF_INFO sfinfo; + int ret = -1; + size_t l; + sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames); + assert(fname && ss && chunk); + + chunk->memblock = NULL; + chunk->index = chunk->length = 0; + + memset(&sfinfo, 0, sizeof(sfinfo)); + + if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) { + pa_log(__FILE__": Failed to open file %s\n", fname); + goto finish; + } + + switch (sfinfo.format & SF_FORMAT_SUBMASK) { + case SF_FORMAT_FLOAT: + case SF_FORMAT_DOUBLE: + /* Only float and double need a special case. */ + ss->format = PA_SAMPLE_FLOAT32NE; + readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float; + break; + default: + /* Everything else is cleanly converted to signed 16 bit. */ + ss->format = PA_SAMPLE_S16NE; + readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short; + break; + } + + ss->rate = sfinfo.samplerate; + ss->channels = sfinfo.channels; + + if (!pa_sample_spec_valid(ss)) { + pa_log(__FILE__": Unsupported sample format in file %s\n", fname); + goto finish; + } + + if ((l = pa_frame_size(ss)*sfinfo.frames) > MAX_FILE_SIZE) { + pa_log(__FILE__": File too large\n"); + goto finish; + } + + chunk->memblock = pa_memblock_new(l, s); + assert(chunk->memblock); + chunk->index = 0; + chunk->length = l; + + if (readf_function(sf, chunk->memblock->data, sfinfo.frames) != sfinfo.frames) { + pa_log(__FILE__": Premature file end\n"); + goto finish; + } + + ret = 0; + +finish: + + if (sf) + sf_close(sf); + + if (ret != 0 && chunk->memblock) + pa_memblock_unref(chunk->memblock); + + return ret; + +} + +int pa_sound_file_too_big_to_cache(const char *fname) { + SNDFILE*sf = NULL; + SF_INFO sfinfo; + pa_sample_spec ss; + + if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) { + pa_log(__FILE__": Failed to open file %s\n", fname); + return 0; + } + + sf_close(sf); + + switch (sfinfo.format & SF_FORMAT_SUBMASK) { + case SF_FORMAT_FLOAT: + case SF_FORMAT_DOUBLE: + /* Only float and double need a special case. */ + ss.format = PA_SAMPLE_FLOAT32NE; + break; + default: + /* Everything else is cleanly converted to signed 16 bit. */ + ss.format = PA_SAMPLE_S16NE; + break; + } + + ss.rate = sfinfo.samplerate; + ss.channels = sfinfo.channels; + + if ((pa_frame_size(&ss) * sfinfo.frames) > MAX_FILE_SIZE) { + pa_log(__FILE__": File too large %s\n", fname); + return 1; + } + + return 0; +} diff --git a/src/polypcore/sound-file.h b/src/polypcore/sound-file.h new file mode 100644 index 00000000..f3003bb9 --- /dev/null +++ b/src/polypcore/sound-file.h @@ -0,0 +1,32 @@ +#ifndef soundfilehfoo +#define soundfilehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "memchunk.h" +#include + +int pa_sound_file_load(const char *fname, pa_sample_spec *ss, pa_memchunk *chunk, pa_memblock_stat *s); + +int pa_sound_file_too_big_to_cache(const char *fname); + +#endif diff --git a/src/polypcore/source-output.c b/src/polypcore/source-output.c new file mode 100644 index 00000000..e1d8ccf7 --- /dev/null +++ b/src/polypcore/source-output.c @@ -0,0 +1,228 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "source-output.h" +#include "xmalloc.h" +#include "subscribe.h" +#include "log.h" + +pa_source_output* pa_source_output_new( + pa_source *s, + const char *driver, + const char *name, + const pa_sample_spec *spec, + const pa_channel_map *map, + int resample_method) { + + pa_source_output *o; + pa_resampler *resampler = NULL; + int r; + char st[256]; + pa_channel_map tmap; + + assert(s); + assert(spec); + assert(s->state == PA_SOURCE_RUNNING); + + if (pa_idxset_size(s->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) { + pa_log(__FILE__": Failed to create source output: too many outputs per source.\n"); + return NULL; + } + + if (resample_method == PA_RESAMPLER_INVALID) + resample_method = s->core->resample_method; + + if (!map) { + pa_channel_map_init_auto(&tmap, spec->channels); + map = &tmap; + } + + if (!pa_sample_spec_equal(&s->sample_spec, spec) || !pa_channel_map_equal(&s->channel_map, map)) + if (!(resampler = pa_resampler_new(&s->sample_spec, &s->channel_map, spec, map, s->core->memblock_stat, resample_method))) + return NULL; + + o = pa_xmalloc(sizeof(pa_source_output)); + o->ref = 1; + o->state = PA_SOURCE_OUTPUT_RUNNING; + o->name = pa_xstrdup(name); + o->driver = pa_xstrdup(driver); + o->owner = NULL; + o->source = s; + o->client = NULL; + + o->sample_spec = *spec; + o->channel_map = *map; + + o->push = NULL; + o->kill = NULL; + o->get_latency = NULL; + o->userdata = NULL; + + o->resampler = resampler; + + assert(s->core); + r = pa_idxset_put(s->core->source_outputs, o, &o->index); + assert(r == 0 && o->index != PA_IDXSET_INVALID); + r = pa_idxset_put(s->outputs, o, NULL); + assert(r == 0); + + pa_sample_spec_snprint(st, sizeof(st), spec); + pa_log_info(__FILE__": created %u \"%s\" on %u with sample spec \"%s\"\n", o->index, o->name, s->index, st); + + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index); + + return o; +} + +void pa_source_output_disconnect(pa_source_output*o) { + assert(o); + assert(o->state != PA_SOURCE_OUTPUT_DISCONNECTED); + assert(o->source); + assert(o->source->core); + + pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL); + pa_idxset_remove_by_data(o->source->outputs, o, NULL); + + pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index); + o->source = NULL; + + o->push = NULL; + o->kill = NULL; + o->get_latency = NULL; + + o->state = PA_SOURCE_OUTPUT_DISCONNECTED; +} + +static void source_output_free(pa_source_output* o) { + assert(o); + + if (o->state != PA_SOURCE_OUTPUT_DISCONNECTED) + pa_source_output_disconnect(o); + + pa_log_info(__FILE__": freed %u \"%s\"\n", o->index, o->name); + + if (o->resampler) + pa_resampler_free(o->resampler); + + pa_xfree(o->name); + pa_xfree(o->driver); + pa_xfree(o); +} + + +void pa_source_output_unref(pa_source_output* o) { + assert(o); + assert(o->ref >= 1); + + if (!(--o->ref)) + source_output_free(o); +} + +pa_source_output* pa_source_output_ref(pa_source_output *o) { + assert(o); + assert(o->ref >= 1); + + o->ref++; + return o; +} + + +void pa_source_output_kill(pa_source_output*o) { + assert(o); + assert(o->ref >= 1); + + if (o->kill) + o->kill(o); +} + +void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { + pa_memchunk rchunk; + + assert(o); + assert(chunk); + assert(chunk->length); + assert(o->push); + + if (o->state == PA_SOURCE_OUTPUT_CORKED) + return; + + if (!o->resampler) { + o->push(o, chunk); + return; + } + + pa_resampler_run(o->resampler, chunk, &rchunk); + if (!rchunk.length) + return; + + assert(rchunk.memblock); + o->push(o, &rchunk); + pa_memblock_unref(rchunk.memblock); +} + +void pa_source_output_set_name(pa_source_output *o, const char *name) { + assert(o); + assert(o->ref >= 1); + + pa_xfree(o->name); + o->name = pa_xstrdup(name); + + pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); +} + +pa_usec_t pa_source_output_get_latency(pa_source_output *o) { + assert(o); + assert(o->ref >= 1); + + if (o->get_latency) + return o->get_latency(o); + + return 0; +} + +void pa_source_output_cork(pa_source_output *o, int b) { + assert(o); + assert(o->ref >= 1); + + if (o->state == PA_SOURCE_OUTPUT_DISCONNECTED) + return; + + o->state = b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING; +} + +pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) { + assert(o); + assert(o->ref >= 1); + + if (!o->resampler) + return PA_RESAMPLER_INVALID; + + return pa_resampler_get_method(o->resampler); +} diff --git a/src/polypcore/source-output.h b/src/polypcore/source-output.h new file mode 100644 index 00000000..4b1cbf15 --- /dev/null +++ b/src/polypcore/source-output.h @@ -0,0 +1,92 @@ +#ifndef foosourceoutputhfoo +#define foosourceoutputhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +typedef struct pa_source_output pa_source_output; + +#include "source.h" +#include +#include "memblockq.h" +#include "resampler.h" +#include "module.h" +#include "client.h" + +typedef enum { + PA_SOURCE_OUTPUT_RUNNING, + PA_SOURCE_OUTPUT_CORKED, + PA_SOURCE_OUTPUT_DISCONNECTED +} pa_source_output_state_t; + +struct pa_source_output { + int ref; + uint32_t index; + pa_source_output_state_t state; + + char *name, *driver; + pa_module *owner; + + pa_source *source; + pa_client *client; + + pa_sample_spec sample_spec; + pa_channel_map channel_map; + + void (*push)(pa_source_output *o, const pa_memchunk *chunk); + void (*kill)(pa_source_output* o); + pa_usec_t (*get_latency) (pa_source_output *i); + + pa_resampler* resampler; + + void *userdata; +}; + +pa_source_output* pa_source_output_new( + pa_source *s, + const char *driver, + const char *name, + const pa_sample_spec *spec, + const pa_channel_map *map, + int resample_method); + +void pa_source_output_unref(pa_source_output* o); +pa_source_output* pa_source_output_ref(pa_source_output *o); + +/* To be called by the implementing module only */ +void pa_source_output_disconnect(pa_source_output*o); + +/* External code may request disconnection with this funcion */ +void pa_source_output_kill(pa_source_output*o); + +void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk); + +void pa_source_output_set_name(pa_source_output *i, const char *name); + +pa_usec_t pa_source_output_get_latency(pa_source_output *i); + +void pa_source_output_cork(pa_source_output *i, int b); + +pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o); + +#endif diff --git a/src/polypcore/source.c b/src/polypcore/source.c new file mode 100644 index 00000000..6e377b20 --- /dev/null +++ b/src/polypcore/source.c @@ -0,0 +1,195 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "source.h" +#include "source-output.h" +#include "namereg.h" +#include "xmalloc.h" +#include "subscribe.h" +#include "log.h" + +pa_source* pa_source_new( + pa_core *core, + const char *driver, + const char *name, + int fail, + const pa_sample_spec *spec, + const pa_channel_map *map) { + + pa_source *s; + char st[256]; + int r; + + assert(core); + assert(name); + assert(*name); + assert(spec); + + s = pa_xnew(pa_source, 1); + + if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SOURCE, s, fail))) { + pa_xfree(s); + return NULL; + } + + s->ref = 1; + s->core = core; + s->state = PA_SOURCE_RUNNING; + s->name = pa_xstrdup(name); + s->description = NULL; + s->driver = pa_xstrdup(driver); + s->owner = NULL; + + s->sample_spec = *spec; + if (map) + s->channel_map = *map; + else + pa_channel_map_init_auto(&s->channel_map, spec->channels); + + s->outputs = pa_idxset_new(NULL, NULL); + s->monitor_of = NULL; + + s->get_latency = NULL; + s->notify = NULL; + s->userdata = NULL; + + r = pa_idxset_put(core->sources, s, &s->index); + assert(s->index != PA_IDXSET_INVALID && r >= 0); + + pa_sample_spec_snprint(st, sizeof(st), spec); + pa_log_info(__FILE__": created %u \"%s\" with sample spec \"%s\"\n", s->index, s->name, st); + + pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index); + + return s; +} + +void pa_source_disconnect(pa_source *s) { + pa_source_output *o, *j = NULL; + + assert(s); + assert(s->state == PA_SOURCE_RUNNING); + + pa_namereg_unregister(s->core, s->name); + + while ((o = pa_idxset_first(s->outputs, NULL))) { + assert(o != j); + pa_source_output_kill(o); + j = o; + } + + pa_idxset_remove_by_data(s->core->sources, s, NULL); + + s->get_latency = NULL; + s->notify = NULL; + + s->state = PA_SOURCE_DISCONNECTED; + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); +} + +static void source_free(pa_source *s) { + assert(s); + assert(!s->ref); + + if (s->state != PA_SOURCE_DISCONNECTED) + pa_source_disconnect(s); + + pa_log_info(__FILE__": freed %u \"%s\"\n", s->index, s->name); + + pa_idxset_free(s->outputs, NULL, NULL); + + pa_xfree(s->name); + pa_xfree(s->description); + pa_xfree(s->driver); + pa_xfree(s); +} + +void pa_source_unref(pa_source *s) { + assert(s); + assert(s->ref >= 1); + + if (!(--s->ref)) + source_free(s); +} + +pa_source* pa_source_ref(pa_source *s) { + assert(s); + assert(s->ref >= 1); + + s->ref++; + return s; +} + +void pa_source_notify(pa_source*s) { + assert(s); + assert(s->ref >= 1); + + if (s->notify) + s->notify(s); +} + +static int do_post(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void*userdata) { + pa_source_output *o = p; + const pa_memchunk *chunk = userdata; + + assert(o); + assert(chunk); + + pa_source_output_push(o, chunk); + return 0; +} + +void pa_source_post(pa_source*s, const pa_memchunk *chunk) { + assert(s); + assert(s->ref >= 1); + assert(chunk); + + pa_source_ref(s); + pa_idxset_foreach(s->outputs, do_post, (void*) chunk); + pa_source_unref(s); +} + +void pa_source_set_owner(pa_source *s, pa_module *m) { + assert(s); + assert(s->ref >= 1); + + s->owner = m; +} + +pa_usec_t pa_source_get_latency(pa_source *s) { + assert(s); + assert(s->ref >= 1); + + if (!s->get_latency) + return 0; + + return s->get_latency(s); +} + diff --git a/src/polypcore/source.h b/src/polypcore/source.h new file mode 100644 index 00000000..be2fb985 --- /dev/null +++ b/src/polypcore/source.h @@ -0,0 +1,86 @@ +#ifndef foosourcehfoo +#define foosourcehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct pa_source pa_source; + +#include +#include "core.h" +#include +#include "idxset.h" +#include "memblock.h" +#include "memchunk.h" +#include "sink.h" +#include +#include "module.h" + +#define PA_MAX_OUTPUTS_PER_SOURCE 16 + +typedef enum pa_source_state { + PA_SOURCE_RUNNING, + PA_SOURCE_DISCONNECTED +} pa_source_state_t; + +struct pa_source { + int ref; + uint32_t index; + pa_core *core; + pa_source_state_t state; + + char *name, *description, *driver; + pa_module *owner; + + pa_sample_spec sample_spec; + pa_channel_map channel_map; + + pa_idxset *outputs; + pa_sink *monitor_of; + + void (*notify)(pa_source*source); + pa_usec_t (*get_latency)(pa_source *s); + + void *userdata; +}; + +pa_source* pa_source_new( + pa_core *core, + const char *driver, + const char *name, + int namereg_fail, + const pa_sample_spec *spec, + const pa_channel_map *map); + +void pa_source_disconnect(pa_source *s); +void pa_source_unref(pa_source *s); +pa_source* pa_source_ref(pa_source *c); + +/* Pass a new memory block to all output streams */ +void pa_source_post(pa_source*s, const pa_memchunk *b); + +void pa_source_notify(pa_source *s); + +void pa_source_set_owner(pa_source *s, pa_module *m); + +pa_usec_t pa_source_get_latency(pa_source *s); + +#endif diff --git a/src/polypcore/strbuf.c b/src/polypcore/strbuf.c new file mode 100644 index 00000000..dcad5e78 --- /dev/null +++ b/src/polypcore/strbuf.c @@ -0,0 +1,167 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include "strbuf.h" + +/* A chunk of the linked list that makes up the string */ +struct chunk { + struct chunk *next; + size_t length; +}; + +#define CHUNK_TO_TEXT(c) ((char*) (c) + sizeof(struct chunk)) + +struct pa_strbuf { + size_t length; + struct chunk *head, *tail; +}; + +pa_strbuf *pa_strbuf_new(void) { + pa_strbuf *sb = pa_xmalloc(sizeof(pa_strbuf)); + sb->length = 0; + sb->head = sb->tail = NULL; + return sb; +} + +void pa_strbuf_free(pa_strbuf *sb) { + assert(sb); + while (sb->head) { + struct chunk *c = sb->head; + sb->head = sb->head->next; + pa_xfree(c); + } + + pa_xfree(sb); +} + +/* Make a C string from the string buffer. The caller has to free + * string with pa_xfree(). */ +char *pa_strbuf_tostring(pa_strbuf *sb) { + char *t, *e; + struct chunk *c; + assert(sb); + + e = t = pa_xmalloc(sb->length+1); + + for (c = sb->head; c; c = c->next) { + assert((size_t) (e-t) <= sb->length); + memcpy(e, CHUNK_TO_TEXT(c), c->length); + e += c->length; + } + + /* Trailing NUL */ + *e = 0; + + assert(e == t+sb->length); + + return t; +} + +/* Combination of pa_strbuf_free() and pa_strbuf_tostring() */ +char *pa_strbuf_tostring_free(pa_strbuf *sb) { + char *t; + assert(sb); + t = pa_strbuf_tostring(sb); + pa_strbuf_free(sb); + return t; +} + +/* Append a string to the string buffer */ +void pa_strbuf_puts(pa_strbuf *sb, const char *t) { + assert(sb && t); + pa_strbuf_putsn(sb, t, strlen(t)); +} + +/* Append a new chunk to the linked list */ +static void append(pa_strbuf *sb, struct chunk *c) { + assert(sb && c); + + if (sb->tail) { + assert(sb->head); + sb->tail->next = c; + } else { + assert(!sb->head); + sb->head = c; + } + + sb->tail = c; + sb->length += c->length; + c->next = NULL; +} + +/* Append up to l bytes of a string to the string buffer */ +void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) { + struct chunk *c; + assert(sb && t); + + if (!l) + return; + + c = pa_xmalloc(sizeof(struct chunk)+l); + c->length = l; + memcpy(CHUNK_TO_TEXT(c), t, l); + + append(sb, c); +} + +/* Append a printf() style formatted string to the string buffer. */ +/* The following is based on an example from the GNU libc documentation */ +int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) { + int size = 100; + struct chunk *c = NULL; + + assert(sb); + + for(;;) { + va_list ap; + int r; + + c = pa_xrealloc(c, sizeof(struct chunk)+size); + + va_start(ap, format); + r = vsnprintf(CHUNK_TO_TEXT(c), size, format, ap); + va_end(ap); + + if (r > -1 && r < size) { + c->length = r; + append(sb, c); + return r; + } + + if (r > -1) /* glibc 2.1 */ + size = r+1; + else /* glibc 2.0 */ + size *= 2; + } +} diff --git a/src/polypcore/strbuf.h b/src/polypcore/strbuf.h new file mode 100644 index 00000000..a88f5190 --- /dev/null +++ b/src/polypcore/strbuf.h @@ -0,0 +1,38 @@ +#ifndef foostrbufhfoo +#define foostrbufhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "gccmacro.h" + +typedef struct pa_strbuf pa_strbuf; + +pa_strbuf *pa_strbuf_new(void); +void pa_strbuf_free(pa_strbuf *sb); +char *pa_strbuf_tostring(pa_strbuf *sb); +char *pa_strbuf_tostring_free(pa_strbuf *sb); + +int pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3); +void pa_strbuf_puts(pa_strbuf *sb, const char *t); +void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t m); + +#endif diff --git a/src/polypcore/strlist.c b/src/polypcore/strlist.c new file mode 100644 index 00000000..09eb0c8a --- /dev/null +++ b/src/polypcore/strlist.c @@ -0,0 +1,137 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "strlist.h" +#include "xmalloc.h" +#include "strbuf.h" +#include "util.h" + +struct pa_strlist { + pa_strlist *next; + char *str; +}; + +pa_strlist* pa_strlist_prepend(pa_strlist *l, const char *s) { + pa_strlist *n; + assert(s); + n = pa_xmalloc(sizeof(pa_strlist)); + n->str = pa_xstrdup(s); + n->next = l; + return n; +} + +char *pa_strlist_tostring(pa_strlist *l) { + int first = 1; + pa_strbuf *b; + + b = pa_strbuf_new(); + for (; l; l = l->next) { + if (!first) + pa_strbuf_puts(b, " "); + first = 0; + pa_strbuf_puts(b, l->str); + } + + return pa_strbuf_tostring_free(b); +} + +pa_strlist* pa_strlist_remove(pa_strlist *l, const char *s) { + pa_strlist *ret = l, *prev = NULL; + assert(l && s); + + while (l) { + if (!strcmp(l->str, s)) { + pa_strlist *n = l->next; + + if (!prev) { + assert(ret == l); + ret = n; + } else + prev->next = n; + + pa_xfree(l->str); + pa_xfree(l); + + l = n; + + } else { + prev = l; + l = l->next; + } + } + + return ret; +} + +void pa_strlist_free(pa_strlist *l) { + while (l) { + pa_strlist *c = l; + l = l->next; + + pa_xfree(c->str); + pa_xfree(c); + } +} + +pa_strlist* pa_strlist_pop(pa_strlist *l, char **s) { + pa_strlist *r; + assert(s); + + if (!l) { + *s = NULL; + return NULL; + } + + *s = l->str; + r = l->next; + pa_xfree(l); + return r; +} + +pa_strlist* pa_strlist_parse(const char *s) { + pa_strlist *head = NULL, *p = NULL; + const char *state = NULL; + char *r; + + while ((r = pa_split_spaces(s, &state))) { + pa_strlist *n; + + n = pa_xmalloc(sizeof(pa_strlist)); + n->str = r; + n->next = NULL; + + if (p) + p->next = n; + else + head = n; + + p = n; + } + + return head; +} diff --git a/src/polypcore/strlist.h b/src/polypcore/strlist.h new file mode 100644 index 00000000..2c54dc74 --- /dev/null +++ b/src/polypcore/strlist.h @@ -0,0 +1,47 @@ +#ifndef foostrlisthfoo +#define foostrlisthfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct pa_strlist pa_strlist; + +/* Add the specified server string to the list, return the new linked list head */ +pa_strlist* pa_strlist_prepend(pa_strlist *l, const char *s); + +/* Remove the specified string from the list, return the new linked list head */ +pa_strlist* pa_strlist_remove(pa_strlist *l, const char *s); + +/* Make a whitespace separated string of all server stringes. Returned memory has to be freed with pa_xfree() */ +char *pa_strlist_tostring(pa_strlist *l); + +/* Free the entire list */ +void pa_strlist_free(pa_strlist *l); + +/* Return the next entry in the list in *string and remove it from + * the list. Returns the new list head. The memory *string points to + * has to be freed with pa_xfree() */ +pa_strlist* pa_strlist_pop(pa_strlist *l, char **s); + +/* Parse a whitespace separated server list */ +pa_strlist* pa_strlist_parse(const char *s); + +#endif diff --git a/src/polypcore/subscribe.c b/src/polypcore/subscribe.c new file mode 100644 index 00000000..e8b3c841 --- /dev/null +++ b/src/polypcore/subscribe.c @@ -0,0 +1,230 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "queue.h" +#include "subscribe.h" +#include "xmalloc.h" +#include "log.h" + +/* The subscription subsystem may be used to be notified whenever an + * entity (sink, source, ...) is created or deleted. Modules may + * register a callback function that is called whenever an event + * matching a subscription mask happens. The execution of the callback + * function is postponed to the next main loop iteration, i.e. is not + * called from within the stack frame the entity was created in. */ + +struct pa_subscription { + pa_core *core; + int dead; + void (*callback)(pa_core *c, pa_subscription_event_type_t t, uint32_t index, void *userdata); + void *userdata; + pa_subscription_mask_t mask; + + pa_subscription *prev, *next; +}; + +struct pa_subscription_event { + pa_subscription_event_type_t type; + uint32_t index; +}; + +static void sched_event(pa_core *c); + +/* Allocate a new subscription object for the given subscription mask. Use the specified callback function and user data */ +pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, void (*callback)(pa_core *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata) { + pa_subscription *s; + assert(c); + + s = pa_xmalloc(sizeof(pa_subscription)); + s->core = c; + s->dead = 0; + s->callback = callback; + s->userdata = userdata; + s->mask = m; + + if ((s->next = c->subscriptions)) + s->next->prev = s; + s->prev = NULL; + c->subscriptions = s; + return s; +} + +/* Free a subscription object, effectively marking it for deletion */ +void pa_subscription_free(pa_subscription*s) { + assert(s && !s->dead); + s->dead = 1; + sched_event(s->core); +} + +static void free_item(pa_subscription *s) { + assert(s && s->core); + + if (s->prev) + s->prev->next = s->next; + else + s->core->subscriptions = s->next; + + if (s->next) + s->next->prev = s->prev; + + pa_xfree(s); +} + +/* Free all subscription objects */ +void pa_subscription_free_all(pa_core *c) { + pa_subscription_event *e; + assert(c); + + while (c->subscriptions) + free_item(c->subscriptions); + + if (c->subscription_event_queue) { + while ((e = pa_queue_pop(c->subscription_event_queue))) + pa_xfree(e); + + pa_queue_free(c->subscription_event_queue, NULL, NULL); + c->subscription_event_queue = NULL; + } + + if (c->subscription_defer_event) { + c->mainloop->defer_free(c->subscription_defer_event); + c->subscription_defer_event = NULL; + } +} + +/*static void dump_event(pa_subscription_event*e) { + switch (e->type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { + case PA_SUBSCRIPTION_EVENT_SINK: + pa_log(__FILE__": SINK_EVENT"); + break; + case PA_SUBSCRIPTION_EVENT_SOURCE: + pa_log(__FILE__": SOURCE_EVENT"); + break; + case PA_SUBSCRIPTION_EVENT_SINK_INPUT: + pa_log(__FILE__": SINK_INPUT_EVENT"); + break; + case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: + pa_log(__FILE__": SOURCE_OUTPUT_EVENT"); + break; + case PA_SUBSCRIPTION_EVENT_MODULE: + pa_log(__FILE__": MODULE_EVENT"); + break; + case PA_SUBSCRIPTION_EVENT_CLIENT: + pa_log(__FILE__": CLIENT_EVENT"); + break; + default: + pa_log(__FILE__": OTHER"); + break; + } + + switch (e->type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) { + case PA_SUBSCRIPTION_EVENT_NEW: + pa_log(__FILE__": NEW"); + break; + case PA_SUBSCRIPTION_EVENT_CHANGE: + pa_log(__FILE__": CHANGE"); + break; + case PA_SUBSCRIPTION_EVENT_REMOVE: + pa_log(__FILE__": REMOVE"); + break; + default: + pa_log(__FILE__": OTHER"); + break; + } + + pa_log(__FILE__": %u\n", e->index); +}*/ + +/* Deferred callback for dispatching subscirption events */ +static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) { + pa_core *c = userdata; + pa_subscription *s; + assert(c && c->subscription_defer_event == de && c->mainloop == m); + + c->mainloop->defer_enable(c->subscription_defer_event, 0); + + + /* Dispatch queued events */ + + if (c->subscription_event_queue) { + pa_subscription_event *e; + + while ((e = pa_queue_pop(c->subscription_event_queue))) { + + for (s = c->subscriptions; s; s = s->next) { + + if (!s->dead && pa_subscription_match_flags(s->mask, e->type)) + s->callback(c, e->type, e->index, s->userdata); + } + + pa_xfree(e); + } + } + + /* Remove dead subscriptions */ + + s = c->subscriptions; + while (s) { + pa_subscription *n = s->next; + if (s->dead) + free_item(s); + s = n; + } +} + +/* Schedule an mainloop event so that a pending subscription event is dispatched */ +static void sched_event(pa_core *c) { + assert(c); + + if (!c->subscription_defer_event) { + c->subscription_defer_event = c->mainloop->defer_new(c->mainloop, defer_cb, c); + assert(c->subscription_defer_event); + } + + c->mainloop->defer_enable(c->subscription_defer_event, 1); +} + +/* Append a new subscription event to the subscription event queue and schedule a main loop event */ +void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t index) { + pa_subscription_event *e; + assert(c); + + e = pa_xmalloc(sizeof(pa_subscription_event)); + e->type = t; + e->index = index; + + if (!c->subscription_event_queue) { + c->subscription_event_queue = pa_queue_new(); + assert(c->subscription_event_queue); + } + + pa_queue_push(c->subscription_event_queue, e); + sched_event(c); +} + + diff --git a/src/polypcore/subscribe.h b/src/polypcore/subscribe.h new file mode 100644 index 00000000..625159e3 --- /dev/null +++ b/src/polypcore/subscribe.h @@ -0,0 +1,37 @@ +#ifndef foosubscribehfoo +#define foosubscribehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct pa_subscription pa_subscription; +typedef struct pa_subscription_event pa_subscription_event; + +#include "core.h" +#include "native-common.h" + +pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, void (*callback)(pa_core *c, pa_subscription_event_type_t t, uint32_t index, void *userdata), void *userdata); +void pa_subscription_free(pa_subscription*s); +void pa_subscription_free_all(pa_core *c); + +void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t idx); + +#endif diff --git a/src/polypcore/tagstruct.c b/src/polypcore/tagstruct.c new file mode 100644 index 00000000..676f67de --- /dev/null +++ b/src/polypcore/tagstruct.c @@ -0,0 +1,609 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#include "winsock.h" + +#include "tagstruct.h" +#include "xmalloc.h" + + +struct pa_tagstruct { + uint8_t *data; + size_t length, allocated; + size_t rindex; + + int dynamic; +}; + +pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) { + pa_tagstruct*t; + + assert(!data || (data && length)); + + t = pa_xmalloc(sizeof(pa_tagstruct)); + t->data = (uint8_t*) data; + t->allocated = t->length = data ? length : 0; + t->rindex = 0; + t->dynamic = !data; + return t; +} + +void pa_tagstruct_free(pa_tagstruct*t) { + assert(t); + if (t->dynamic) + pa_xfree(t->data); + pa_xfree(t); +} + +uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l) { + uint8_t *p; + assert(t && t->dynamic && l); + p = t->data; + *l = t->length; + pa_xfree(t); + return p; +} + +static void extend(pa_tagstruct*t, size_t l) { + assert(t && t->dynamic); + + if (t->length+l <= t->allocated) + return; + + t->data = pa_xrealloc(t->data, t->allocated = t->length+l+100); +} + + +void pa_tagstruct_puts(pa_tagstruct*t, const char *s) { + size_t l; + assert(t); + if (s) { + l = strlen(s)+2; + extend(t, l); + t->data[t->length] = PA_TAG_STRING; + strcpy((char*) (t->data+t->length+1), s); + t->length += l; + } else { + extend(t, 1); + t->data[t->length] = PA_TAG_STRING_NULL; + t->length += 1; + } +} + +void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) { + assert(t); + extend(t, 5); + t->data[t->length] = PA_TAG_U32; + i = htonl(i); + memcpy(t->data+t->length+1, &i, 4); + t->length += 5; +} + +void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) { + assert(t); + extend(t, 2); + t->data[t->length] = PA_TAG_U8; + *(t->data+t->length+1) = c; + t->length += 2; +} + +void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) { + uint32_t rate; + assert(t && ss); + extend(t, 7); + t->data[t->length] = PA_TAG_SAMPLE_SPEC; + t->data[t->length+1] = (uint8_t) ss->format; + t->data[t->length+2] = ss->channels; + rate = htonl(ss->rate); + memcpy(t->data+t->length+3, &rate, 4); + t->length += 7; +} + +void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) { + uint32_t tmp; + assert(t && p); + + extend(t, 5+length); + t->data[t->length] = PA_TAG_ARBITRARY; + tmp = htonl(length); + memcpy(t->data+t->length+1, &tmp, 4); + if (length) + memcpy(t->data+t->length+5, p, length); + t->length += 5+length; +} + +void pa_tagstruct_put_boolean(pa_tagstruct*t, int b) { + assert(t); + extend(t, 1); + t->data[t->length] = b ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE; + t->length += 1; +} + +void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) { + uint32_t tmp; + assert(t); + extend(t, 9); + t->data[t->length] = PA_TAG_TIMEVAL; + tmp = htonl(tv->tv_sec); + memcpy(t->data+t->length+1, &tmp, 4); + tmp = htonl(tv->tv_usec); + memcpy(t->data+t->length+5, &tmp, 4); + t->length += 9; +} + +void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) { + uint32_t tmp; + assert(t); + extend(t, 9); + t->data[t->length] = PA_TAG_USEC; + tmp = htonl((uint32_t) (u >> 32)); + memcpy(t->data+t->length+1, &tmp, 4); + tmp = htonl((uint32_t) u); + memcpy(t->data+t->length+5, &tmp, 4); + t->length += 9; +} + +void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) { + uint32_t tmp; + assert(t); + extend(t, 9); + t->data[t->length] = PA_TAG_U64; + tmp = htonl((uint32_t) (u >> 32)); + memcpy(t->data+t->length+1, &tmp, 4); + tmp = htonl((uint32_t) u); + memcpy(t->data+t->length+5, &tmp, 4); + t->length += 9; +} + +void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map) { + unsigned i; + + assert(t); + extend(t, 2 + map->channels); + + t->data[t->length++] = PA_TAG_CHANNEL_MAP; + t->data[t->length++] = map->channels; + + for (i = 0; i < map->channels; i ++) + t->data[t->length++] = (uint8_t) map->map[i]; +} + +void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) { + unsigned i; + pa_volume_t vol; + + assert(t); + extend(t, 2 + cvolume->channels * sizeof(pa_volume_t)); + + t->data[t->length++] = PA_TAG_CVOLUME; + t->data[t->length++] = cvolume->channels; + + for (i = 0; i < cvolume->channels; i ++) { + vol = htonl(cvolume->values[i]); + memcpy(t->data + t->length, &vol, sizeof(pa_volume_t)); + t->length += sizeof(pa_volume_t); + } +} + +int pa_tagstruct_gets(pa_tagstruct*t, const char **s) { + int error = 0; + size_t n; + char *c; + assert(t && s); + + if (t->rindex+1 > t->length) + return -1; + + if (t->data[t->rindex] == PA_TAG_STRING_NULL) { + t->rindex++; + *s = NULL; + return 0; + } + + if (t->rindex+2 > t->length) + return -1; + + if (t->data[t->rindex] != PA_TAG_STRING) + return -1; + + error = 1; + for (n = 0, c = (char*) (t->data+t->rindex+1); t->rindex+1+n < t->length; n++, c++) + if (!*c) { + error = 0; + break; + } + + if (error) + return -1; + + *s = (char*) (t->data+t->rindex+1); + + t->rindex += n+2; + return 0; +} + +int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i) { + assert(t && i); + + if (t->rindex+5 > t->length) + return -1; + + if (t->data[t->rindex] != PA_TAG_U32) + return -1; + + memcpy(i, t->data+t->rindex+1, 4); + *i = ntohl(*i); + t->rindex += 5; + return 0; +} + +int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c) { + assert(t && c); + + if (t->rindex+2 > t->length) + return -1; + + if (t->data[t->rindex] != PA_TAG_U8) + return -1; + + *c = t->data[t->rindex+1]; + t->rindex +=2; + return 0; +} + +int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss) { + assert(t && ss); + + if (t->rindex+7 > t->length) + return -1; + + if (t->data[t->rindex] != PA_TAG_SAMPLE_SPEC) + return -1; + + ss->format = t->data[t->rindex+1]; + ss->channels = t->data[t->rindex+2]; + memcpy(&ss->rate, t->data+t->rindex+3, 4); + ss->rate = ntohl(ss->rate); + + if (!pa_sample_spec_valid(ss)) + return -1; + + t->rindex += 7; + return 0; +} + +int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) { + uint32_t len; + assert(t && p); + + if (t->rindex+5+length > t->length) + return -1; + + if (t->data[t->rindex] != PA_TAG_ARBITRARY) + return -1; + + memcpy(&len, t->data+t->rindex+1, 4); + if (ntohl(len) != length) + return -1; + + *p = t->data+t->rindex+5; + t->rindex += 5+length; + return 0; +} + +int pa_tagstruct_eof(pa_tagstruct*t) { + assert(t); + return t->rindex >= t->length; +} + +const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) { + assert(t && t->dynamic && l); + *l = t->length; + return t->data; +} + +int pa_tagstruct_get_boolean(pa_tagstruct*t, int *b) { + assert(t && b); + + if (t->rindex+1 > t->length) + return -1; + + if (t->data[t->rindex] == PA_TAG_BOOLEAN_TRUE) + *b = 1; + else if (t->data[t->rindex] == PA_TAG_BOOLEAN_FALSE) + *b = 0; + else + return -1; + + t->rindex +=1; + return 0; +} + +int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) { + + if (t->rindex+9 > t->length) + return -1; + + if (t->data[t->rindex] != PA_TAG_TIMEVAL) + return -1; + + memcpy(&tv->tv_sec, t->data+t->rindex+1, 4); + tv->tv_sec = ntohl(tv->tv_sec); + memcpy(&tv->tv_usec, t->data+t->rindex+5, 4); + tv->tv_usec = ntohl(tv->tv_usec); + t->rindex += 9; + return 0; + +} + +int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u) { + uint32_t tmp; + assert(t && u); + + if (t->rindex+9 > t->length) + return -1; + + if (t->data[t->rindex] != PA_TAG_USEC) + return -1; + + memcpy(&tmp, t->data+t->rindex+1, 4); + *u = (pa_usec_t) ntohl(tmp) << 32; + memcpy(&tmp, t->data+t->rindex+5, 4); + *u |= (pa_usec_t) ntohl(tmp); + t->rindex +=9; + return 0; +} + +int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) { + uint32_t tmp; + assert(t && u); + + if (t->rindex+9 > t->length) + return -1; + + if (t->data[t->rindex] != PA_TAG_U64) + return -1; + + memcpy(&tmp, t->data+t->rindex+1, 4); + *u = (pa_usec_t) ntohl(tmp) << 32; + memcpy(&tmp, t->data+t->rindex+5, 4); + *u |= (pa_usec_t) ntohl(tmp); + t->rindex +=9; + return 0; +} + +int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map) { + unsigned i; + + assert(t); + assert(map); + + if (t->rindex+2 > t->length) + return -1; + + if (t->data[t->rindex] != PA_TAG_CHANNEL_MAP) + return -1; + + if ((map->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX) + return -1; + + if (t->rindex+2+map->channels > t->length) + return -1; + + for (i = 0; i < map->channels; i ++) + map->map[i] = (int8_t) t->data[t->rindex + 2 + i]; + + if (!pa_channel_map_valid(map)) + return -1; + + t->rindex += 2 + map->channels; + return 0; +} + +int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) { + unsigned i; + pa_volume_t vol; + + assert(t); + assert(cvolume); + + if (t->rindex+2 > t->length) + return -1; + + if (t->data[t->rindex] != PA_TAG_CVOLUME) + return -1; + + if ((cvolume->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX) + return -1; + + if (t->rindex+2+cvolume->channels*sizeof(pa_volume_t) > t->length) + return -1; + + for (i = 0; i < cvolume->channels; i ++) { + memcpy(&vol, t->data + t->rindex + 2 + i * sizeof(pa_volume_t), sizeof(pa_volume_t)); + cvolume->values[i] = (pa_volume_t) ntohl(vol); + } + + if (!pa_cvolume_valid(cvolume)) + return -1; + + t->rindex += 2 + cvolume->channels * sizeof(pa_volume_t); + return 0; +} + +void pa_tagstruct_put(pa_tagstruct *t, ...) { + va_list va; + assert(t); + + va_start(va, t); + + for (;;) { + int tag = va_arg(va, int); + + if (tag == PA_TAG_INVALID) + break; + + switch (tag) { + case PA_TAG_STRING: + case PA_TAG_STRING_NULL: + pa_tagstruct_puts(t, va_arg(va, char*)); + break; + + case PA_TAG_U32: + pa_tagstruct_putu32(t, va_arg(va, uint32_t)); + break; + + case PA_TAG_U8: + pa_tagstruct_putu8(t, (uint8_t) va_arg(va, int)); + break; + + case PA_TAG_U64: + pa_tagstruct_putu64(t, va_arg(va, uint64_t)); + break; + + case PA_TAG_SAMPLE_SPEC: + pa_tagstruct_put_sample_spec(t, va_arg(va, pa_sample_spec*)); + break; + + case PA_TAG_ARBITRARY: { + void *p = va_arg(va, void*); + size_t size = va_arg(va, size_t); + pa_tagstruct_put_arbitrary(t, p, size); + break; + } + + case PA_TAG_BOOLEAN_TRUE: + case PA_TAG_BOOLEAN_FALSE: + pa_tagstruct_put_boolean(t, va_arg(va, int)); + break; + + case PA_TAG_TIMEVAL: + pa_tagstruct_put_timeval(t, va_arg(va, struct timeval*)); + break; + + case PA_TAG_USEC: + pa_tagstruct_put_usec(t, va_arg(va, pa_usec_t)); + break; + + case PA_TAG_CHANNEL_MAP: + pa_tagstruct_put_channel_map(t, va_arg(va, pa_channel_map *)); + break; + + case PA_TAG_CVOLUME: + pa_tagstruct_put_cvolume(t, va_arg(va, pa_cvolume *)); + break; + + default: + abort(); + } + } + + va_end(va); +} + +int pa_tagstruct_get(pa_tagstruct *t, ...) { + va_list va; + int ret = 0; + + assert(t); + + va_start(va, t); + while (ret == 0) { + int tag = va_arg(va, int); + + if (tag == PA_TAG_INVALID) + break; + + switch (tag) { + case PA_TAG_STRING: + case PA_TAG_STRING_NULL: + ret = pa_tagstruct_gets(t, va_arg(va, const char**)); + break; + + case PA_TAG_U32: + ret = pa_tagstruct_getu32(t, va_arg(va, uint32_t*)); + break; + + case PA_TAG_U8: + ret = pa_tagstruct_getu8(t, va_arg(va, uint8_t*)); + break; + + case PA_TAG_U64: + ret = pa_tagstruct_getu64(t, va_arg(va, uint64_t*)); + break; + + case PA_TAG_SAMPLE_SPEC: + ret = pa_tagstruct_get_sample_spec(t, va_arg(va, pa_sample_spec*)); + break; + + case PA_TAG_ARBITRARY: { + const void **p = va_arg(va, const void**); + size_t size = va_arg(va, size_t); + ret = pa_tagstruct_get_arbitrary(t, p, size); + break; + } + + case PA_TAG_BOOLEAN_TRUE: + case PA_TAG_BOOLEAN_FALSE: + ret = pa_tagstruct_get_boolean(t, va_arg(va, int*)); + break; + + case PA_TAG_TIMEVAL: + ret = pa_tagstruct_get_timeval(t, va_arg(va, struct timeval*)); + break; + + case PA_TAG_USEC: + ret = pa_tagstruct_get_usec(t, va_arg(va, pa_usec_t*)); + break; + + case PA_TAG_CHANNEL_MAP: + ret = pa_tagstruct_get_channel_map(t, va_arg(va, pa_channel_map *)); + break; + + case PA_TAG_CVOLUME: + ret = pa_tagstruct_get_cvolume(t, va_arg(va, pa_cvolume *)); + break; + + + default: + abort(); + } + + } + + va_end(va); + return ret; +} diff --git a/src/polypcore/tagstruct.h b/src/polypcore/tagstruct.h new file mode 100644 index 00000000..59db67b0 --- /dev/null +++ b/src/polypcore/tagstruct.h @@ -0,0 +1,92 @@ +#ifndef footagstructhfoo +#define footagstructhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include +#include + +#include +#include +#include + +typedef struct pa_tagstruct pa_tagstruct; + +enum { + PA_TAG_INVALID = 0, + PA_TAG_STRING = 't', + PA_TAG_STRING_NULL = 'N', + PA_TAG_U32 = 'L', + PA_TAG_U8 = 'B', + PA_TAG_U64 = 'R', + PA_TAG_SAMPLE_SPEC = 'a', + PA_TAG_ARBITRARY = 'x', + PA_TAG_BOOLEAN_TRUE = '1', + PA_TAG_BOOLEAN_FALSE = '0', + PA_TAG_BOOLEAN = PA_TAG_BOOLEAN_TRUE, + PA_TAG_TIMEVAL = 'T', + PA_TAG_USEC = 'U' /* 64bit unsigned */, + PA_TAG_CHANNEL_MAP = 'm', + PA_TAG_CVOLUME = 'v' +}; + + + +pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length); +void pa_tagstruct_free(pa_tagstruct*t); +uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l); + +int pa_tagstruct_eof(pa_tagstruct*t); +const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l); + +void pa_tagstruct_put(pa_tagstruct *t, ...); + +void pa_tagstruct_puts(pa_tagstruct*t, const char *s); +void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c); +void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i); +void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t i); +void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss); +void pa_tagstruct_put_arbitrary(pa_tagstruct*t, const void *p, size_t length); +void pa_tagstruct_put_boolean(pa_tagstruct*t, int b); +void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv); +void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u); +void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map); +void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume); + +int pa_tagstruct_get(pa_tagstruct *t, ...); + +int pa_tagstruct_gets(pa_tagstruct*t, const char **s); +int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c); +int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i); +int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *i); +int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss); +int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length); +int pa_tagstruct_get_boolean(pa_tagstruct *t, int *b); +int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv); +int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u); +int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map); +int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *v); + + +#endif diff --git a/src/polypcore/tokenizer.c b/src/polypcore/tokenizer.c new file mode 100644 index 00000000..5e0c1b16 --- /dev/null +++ b/src/polypcore/tokenizer.c @@ -0,0 +1,88 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "tokenizer.h" +#include "dynarray.h" +#include "xmalloc.h" +#include "gccmacro.h" + +struct pa_tokenizer { + pa_dynarray *dynarray; +}; + +static void token_free(void *p, PA_GCC_UNUSED void *userdata) { + pa_xfree(p); +} + +static void parse(pa_dynarray*a, const char *s, unsigned args) { + int infty = 0; + const char delimiter[] = " \t\n\r"; + const char *p; + assert(a && s); + + if (args == 0) + infty = 1; + + p = s+strspn(s, delimiter); + while (*p && (infty || args >= 2)) { + size_t l = strcspn(p, delimiter); + char *n = pa_xstrndup(p, l); + pa_dynarray_append(a, n); + p += l; + p += strspn(p, delimiter); + args--; + } + + if (args && *p) { + char *n = pa_xstrdup(p); + pa_dynarray_append(a, n); + } +} + +pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args) { + pa_tokenizer *t; + + t = pa_xmalloc(sizeof(pa_tokenizer)); + t->dynarray = pa_dynarray_new(); + assert(t->dynarray); + + parse(t->dynarray, s, args); + return t; +} + +void pa_tokenizer_free(pa_tokenizer *t) { + assert(t); + pa_dynarray_free(t->dynarray, token_free, NULL); + pa_xfree(t); +} + +const char *pa_tokenizer_get(pa_tokenizer *t, unsigned i) { + assert(t); + return pa_dynarray_get(t->dynarray, i); +} diff --git a/src/polypcore/tokenizer.h b/src/polypcore/tokenizer.h new file mode 100644 index 00000000..bedacb8a --- /dev/null +++ b/src/polypcore/tokenizer.h @@ -0,0 +1,32 @@ +#ifndef footokenizerhfoo +#define footokenizerhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct pa_tokenizer pa_tokenizer; + +pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args); +void pa_tokenizer_free(pa_tokenizer *t); + +const char *pa_tokenizer_get(pa_tokenizer *t, unsigned i); + +#endif diff --git a/src/polypcore/util.c b/src/polypcore/util.c new file mode 100644 index 00000000..4b6edb97 --- /dev/null +++ b/src/polypcore/util.c @@ -0,0 +1,1176 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SCHED_H +#include +#endif + +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif + +#ifdef HAVE_PTHREAD +#include +#endif + +#ifdef HAVE_NETDB_H +#include +#endif + +#ifdef HAVE_WINDOWS_H +#include +#endif + +#include + +#ifdef HAVE_PWD_H +#include +#endif +#ifdef HAVE_GRP_H +#include +#endif + +#include "winsock.h" + +#include "util.h" +#include "xmalloc.h" +#include "log.h" + +#ifndef OS_IS_WIN32 +#define PA_RUNTIME_PATH_PREFIX "/tmp/polypaudio-" +#define PATH_SEP '/' +#else +#define PA_RUNTIME_PATH_PREFIX "%TEMP%\\polypaudio-" +#define PATH_SEP '\\' +#endif + +#ifdef OS_IS_WIN32 + +#define POLYP_ROOTENV "POLYP_ROOT" + +int pa_set_root(HANDLE handle) { + char library_path[MAX_PATH + sizeof(POLYP_ROOTENV) + 1], *sep; + + strcpy(library_path, POLYP_ROOTENV "="); + + if (!GetModuleFileName(handle, library_path + sizeof(POLYP_ROOTENV), MAX_PATH)) + return 0; + + sep = strrchr(library_path, '\\'); + if (sep) + *sep = '\0'; + + if (_putenv(library_path) < 0) + return 0; + + return 1; +} + +#endif + +/** Make a file descriptor nonblock. Doesn't do any error checking */ +void pa_make_nonblock_fd(int fd) { +#ifdef O_NONBLOCK + int v; + assert(fd >= 0); + + if ((v = fcntl(fd, F_GETFL)) >= 0) + if (!(v & O_NONBLOCK)) + fcntl(fd, F_SETFL, v|O_NONBLOCK); +#elif defined(OS_IS_WIN32) + u_long arg = 1; + if (ioctlsocket(fd, FIONBIO, &arg) < 0) { + if (WSAGetLastError() == WSAENOTSOCK) + pa_log_warn(__FILE__": WARNING: Only sockets can be made non-blocking!\n"); + } +#else + pa_log_warn(__FILE__": WARNING: Non-blocking I/O not supported.!\n"); +#endif +} + +/** Creates a directory securely */ +int pa_make_secure_dir(const char* dir) { + struct stat st; + assert(dir); + +#ifdef OS_IS_WIN32 + if (mkdir(dir) < 0) +#else + if (mkdir(dir, 0700) < 0) +#endif + if (errno != EEXIST) + return -1; + +#ifdef HAVE_LSTAT + if (lstat(dir, &st) < 0) +#else + if (stat(dir, &st) < 0) +#endif + goto fail; + +#ifndef OS_IS_WIN32 + if (!S_ISDIR(st.st_mode) || (st.st_uid != getuid()) || ((st.st_mode & 0777) != 0700)) + goto fail; +#else + fprintf(stderr, "FIXME: pa_make_secure_dir()\n"); +#endif + + return 0; + +fail: + rmdir(dir); + return -1; +} + +/* Creates a the parent directory of the specified path securely */ +int pa_make_secure_parent_dir(const char *fn) { + int ret = -1; + char *slash, *dir = pa_xstrdup(fn); + + slash = pa_path_get_filename(dir); + if (slash == fn) + goto finish; + *(slash-1) = 0; + + if (pa_make_secure_dir(dir) < 0) + goto finish; + + ret = 0; + +finish: + pa_xfree(dir); + return ret; +} + + +/** Calls read() in a loop. Makes sure that as much as 'size' bytes, + * unless EOF is reached or an error occured */ +ssize_t pa_loop_read(int fd, void*data, size_t size) { + ssize_t ret = 0; + assert(fd >= 0 && data && size); + + while (size > 0) { + ssize_t r; + + if ((r = read(fd, data, size)) < 0) + return r; + + if (r == 0) + break; + + ret += r; + data = (uint8_t*) data + r; + size -= r; + } + + return ret; +} + +/** Similar to pa_loop_read(), but wraps write() */ +ssize_t pa_loop_write(int fd, const void*data, size_t size) { + ssize_t ret = 0; + assert(fd >= 0 && data && size); + + while (size > 0) { + ssize_t r; + + if ((r = write(fd, data, size)) < 0) + return r; + + if (r == 0) + break; + + ret += r; + data = (const uint8_t*) data + r; + size -= r; + } + + return ret; +} + +/* Print a warning messages in case that the given signal is not + * blocked or trapped */ +void pa_check_signal_is_blocked(int sig) { +#ifdef HAVE_SIGACTION + struct sigaction sa; + sigset_t set; + + /* If POSIX threads are supported use thread-aware + * pthread_sigmask() function, to check if the signal is + * blocked. Otherwise fall back to sigprocmask() */ + +#ifdef HAVE_PTHREAD + if (pthread_sigmask(SIG_SETMASK, NULL, &set) < 0) { +#endif + if (sigprocmask(SIG_SETMASK, NULL, &set) < 0) { + pa_log(__FILE__": sigprocmask() failed: %s\n", strerror(errno)); + return; + } +#ifdef HAVE_PTHREAD + } +#endif + + if (sigismember(&set, sig)) + return; + + /* Check whether the signal is trapped */ + + if (sigaction(sig, NULL, &sa) < 0) { + pa_log(__FILE__": sigaction() failed: %s\n", strerror(errno)); + return; + } + + if (sa.sa_handler != SIG_DFL) + return; + + pa_log(__FILE__": WARNING: %s is not trapped. This might cause malfunction!\n", pa_strsignal(sig)); +#else /* HAVE_SIGACTION */ + pa_log(__FILE__": WARNING: %s might not be trapped. This might cause malfunction!\n", pa_strsignal(sig)); +#endif +} + +/* The following function is based on an example from the GNU libc + * documentation. This function is similar to GNU's asprintf(). */ +char *pa_sprintf_malloc(const char *format, ...) { + int size = 100; + char *c = NULL; + + assert(format); + + for(;;) { + int r; + va_list ap; + + c = pa_xrealloc(c, size); + + va_start(ap, format); + r = vsnprintf(c, size, format, ap); + va_end(ap); + + if (r > -1 && r < size) + return c; + + if (r > -1) /* glibc 2.1 */ + size = r+1; + else /* glibc 2.0 */ + size *= 2; + } +} + +/* Same as the previous function, but use a va_list instead of an + * ellipsis */ +char *pa_vsprintf_malloc(const char *format, va_list ap) { + int size = 100; + char *c = NULL; + + assert(format); + + for(;;) { + int r; + c = pa_xrealloc(c, size); + r = vsnprintf(c, size, format, ap); + + if (r > -1 && r < size) + return c; + + if (r > -1) /* glibc 2.1 */ + size = r+1; + else /* glibc 2.0 */ + size *= 2; + } +} + +/* Return the current username in the specified string buffer. */ +char *pa_get_user_name(char *s, size_t l) { + char *p; + char buf[1024]; + +#ifdef HAVE_PWD_H + struct passwd pw, *r; +#endif + + assert(s && l > 0); + + if (!(p = getenv("USER")) && !(p = getenv("LOGNAME")) && !(p = getenv("USERNAME"))) { +#ifdef HAVE_PWD_H + +#ifdef HAVE_GETPWUID_R + if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { +#else + /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) + * that do not support getpwuid_r. */ + if ((r = getpwuid(getuid())) == NULL) { +#endif + snprintf(s, l, "%lu", (unsigned long) getuid()); + return s; + } + + p = r->pw_name; + +#elif defined(OS_IS_WIN32) /* HAVE_PWD_H */ + DWORD size = sizeof(buf); + + if (!GetUserName(buf, &size)) + return NULL; + + p = buf; + +#else /* HAVE_PWD_H */ + return NULL; +#endif /* HAVE_PWD_H */ + } + + return pa_strlcpy(s, p, l); +} + +/* Return the current hostname in the specified buffer. */ +char *pa_get_host_name(char *s, size_t l) { + assert(s && l > 0); + if (gethostname(s, l) < 0) { + pa_log(__FILE__": gethostname(): %s\n", strerror(errno)); + return NULL; + } + s[l-1] = 0; + return s; +} + +/* Return the home directory of the current user */ +char *pa_get_home_dir(char *s, size_t l) { + char *e; + +#ifdef HAVE_PWD_H + char buf[1024]; + struct passwd pw, *r; +#endif + + assert(s && l); + + if ((e = getenv("HOME"))) + return pa_strlcpy(s, e, l); + + if ((e = getenv("USERPROFILE"))) + return pa_strlcpy(s, e, l); + +#ifdef HAVE_PWD_H +#ifdef HAVE_GETPWUID_R + if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { + pa_log(__FILE__": getpwuid_r() failed\n"); +#else + /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) + * that do not support getpwuid_r. */ + if ((r = getpwuid(getuid())) == NULL) { + pa_log(__FILE__": getpwuid_r() failed\n"); +#endif + return NULL; + } + + return pa_strlcpy(s, r->pw_dir, l); +#else /* HAVE_PWD_H */ + return NULL; +#endif +} + +/* Similar to OpenBSD's strlcpy() function */ +char *pa_strlcpy(char *b, const char *s, size_t l) { + assert(b && s && l > 0); + + strncpy(b, s, l); + b[l-1] = 0; + return b; +} + +int pa_gettimeofday(struct timeval *tv) { +#ifdef HAVE_GETTIMEOFDAY + return gettimeofday(tv, NULL); +#elif defined(OS_IS_WIN32) + /* + * Copied from implementation by Steven Edwards (LGPL). + * Found on wine mailing list. + */ + +#if defined(_MSC_VER) || defined(__BORLANDC__) +#define EPOCHFILETIME (116444736000000000i64) +#else +#define EPOCHFILETIME (116444736000000000LL) +#endif + + FILETIME ft; + LARGE_INTEGER li; + __int64 t; + + if (tv) { + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + t = li.QuadPart; /* In 100-nanosecond intervals */ + t -= EPOCHFILETIME; /* Offset to the Epoch time */ + t /= 10; /* In microseconds */ + tv->tv_sec = (long)(t / 1000000); + tv->tv_usec = (long)(t % 1000000); + } + + return 0; +#else +#error "Platform lacks gettimeofday() or equivalent function." +#endif +} + +/* Calculate the difference between the two specfified timeval + * timestamsps. */ +pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) { + pa_usec_t r; + assert(a && b); + + /* Check which whan is the earlier time and swap the two arguments if reuqired. */ + if (pa_timeval_cmp(a, b) < 0) { + const struct timeval *c; + c = a; + a = b; + b = c; + } + + /* Calculate the second difference*/ + r = ((pa_usec_t) a->tv_sec - b->tv_sec)* 1000000; + + /* Calculate the microsecond difference */ + if (a->tv_usec > b->tv_usec) + r += ((pa_usec_t) a->tv_usec - b->tv_usec); + else if (a->tv_usec < b->tv_usec) + r -= ((pa_usec_t) b->tv_usec - a->tv_usec); + + return r; +} + +/* Compare the two timeval structs and return 0 when equal, negative when a < b, positive otherwse */ +int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) { + assert(a && b); + + if (a->tv_sec < b->tv_sec) + return -1; + + if (a->tv_sec > b->tv_sec) + return 1; + + if (a->tv_usec < b->tv_usec) + return -1; + + if (a->tv_usec > b->tv_usec) + return 1; + + return 0; +} + +/* Return the time difference between now and the specified timestamp */ +pa_usec_t pa_timeval_age(const struct timeval *tv) { + struct timeval now; + assert(tv); + pa_gettimeofday(&now); + return pa_timeval_diff(&now, tv); +} + +/* Add the specified time inmicroseconds to the specified timeval structure */ +void pa_timeval_add(struct timeval *tv, pa_usec_t v) { + unsigned long secs; + assert(tv); + + secs = (v/1000000); + tv->tv_sec += (unsigned long) secs; + v -= secs*1000000; + + tv->tv_usec += v; + + /* Normalize */ + while (tv->tv_usec >= 1000000) { + tv->tv_sec++; + tv->tv_usec -= 1000000; + } +} + +#define NICE_LEVEL (-15) + +/* Raise the priority of the current process as much as possible and +sensible: set the nice level to -15 and enable realtime scheduling if +supported.*/ +void pa_raise_priority(void) { + +#ifdef HAVE_SYS_RESOURCE_H + if (setpriority(PRIO_PROCESS, 0, NICE_LEVEL) < 0) + pa_log_warn(__FILE__": setpriority() failed: %s\n", strerror(errno)); + else + pa_log_info(__FILE__": Successfully gained nice level %i.\n", NICE_LEVEL); +#endif + +#ifdef _POSIX_PRIORITY_SCHEDULING + { + struct sched_param sp; + + if (sched_getparam(0, &sp) < 0) { + pa_log(__FILE__": sched_getparam() failed: %s\n", strerror(errno)); + return; + } + + sp.sched_priority = 1; + if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) { + pa_log_warn(__FILE__": sched_setscheduler() failed: %s\n", strerror(errno)); + return; + } + + pa_log_info(__FILE__": Successfully enabled SCHED_FIFO scheduling.\n"); + } +#endif + +#ifdef OS_IS_WIN32 + if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) + pa_log_warn(__FILE__": SetPriorityClass() failed: 0x%08X\n", GetLastError()); + else + pa_log_info(__FILE__": Successfully gained high priority class.\n"); +#endif +} + +/* Reset the priority to normal, inverting the changes made by pa_raise_priority() */ +void pa_reset_priority(void) { +#ifdef OS_IS_WIN32 + SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); +#endif + +#ifdef _POSIX_PRIORITY_SCHEDULING + { + struct sched_param sp; + sched_getparam(0, &sp); + sp.sched_priority = 0; + sched_setscheduler(0, SCHED_OTHER, &sp); + } +#endif + +#ifdef HAVE_SYS_RESOURCE_H + setpriority(PRIO_PROCESS, 0, 0); +#endif +} + +/* Set the FD_CLOEXEC flag for a fd */ +int pa_fd_set_cloexec(int fd, int b) { + +#ifdef FD_CLOEXEC + int v; + assert(fd >= 0); + + if ((v = fcntl(fd, F_GETFD, 0)) < 0) + return -1; + + v = (v & ~FD_CLOEXEC) | (b ? FD_CLOEXEC : 0); + + if (fcntl(fd, F_SETFD, v) < 0) + return -1; +#endif + + return 0; +} + +/* Return the binary file name of the current process. Works on Linux + * only. This shoul be used for eyecandy only, don't rely on return + * non-NULL! */ +char *pa_get_binary_name(char *s, size_t l) { + +#ifdef HAVE_READLINK + char path[PATH_MAX]; + int i; + assert(s && l); + + /* This works on Linux only */ + + snprintf(path, sizeof(path), "/proc/%u/exe", (unsigned) getpid()); + if ((i = readlink(path, s, l-1)) < 0) + return NULL; + + s[i] = 0; + return s; +#elif defined(OS_IS_WIN32) + char path[PATH_MAX]; + if (!GetModuleFileName(NULL, path, PATH_MAX)) + return NULL; + pa_strlcpy(s, pa_path_get_filename(path), l); + return s; +#else + return NULL; +#endif +} + +/* Return a pointer to the filename inside a path (which is the last + * component). */ +char *pa_path_get_filename(const char *p) { + char *fn; + + if ((fn = strrchr(p, PATH_SEP))) + return fn+1; + + return (char*) p; +} + +/* Try to parse a boolean string value.*/ +int pa_parse_boolean(const char *v) { + + if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on")) + return 1; + else if (!strcmp(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off")) + return 0; + + return -1; +} + +/* Split the specified string wherever one of the strings in delimiter + * occurs. Each time it is called returns a newly allocated string + * with pa_xmalloc(). The variable state points to, should be + * initiallized to NULL before the first call. */ +char *pa_split(const char *c, const char *delimiter, const char**state) { + const char *current = *state ? *state : c; + size_t l; + + if (!*current) + return NULL; + + l = strcspn(current, delimiter); + *state = current+l; + + if (**state) + (*state)++; + + return pa_xstrndup(current, l); +} + +/* What is interpreted as whitespace? */ +#define WHITESPACE " \t\n" + +/* Split a string into words. Otherwise similar to pa_split(). */ +char *pa_split_spaces(const char *c, const char **state) { + const char *current = *state ? *state : c; + size_t l; + + if (!*current || *c == 0) + return NULL; + + current += strspn(current, WHITESPACE); + l = strcspn(current, WHITESPACE); + + *state = current+l; + + return pa_xstrndup(current, l); +} + +/* Return the name of an UNIX signal. Similar to GNU's strsignal() */ +const char *pa_strsignal(int sig) { + switch(sig) { + case SIGINT: return "SIGINT"; + case SIGTERM: return "SIGTERM"; +#ifdef SIGUSR1 + case SIGUSR1: return "SIGUSR1"; +#endif +#ifdef SIGUSR2 + case SIGUSR2: return "SIGUSR2"; +#endif +#ifdef SIGXCPU + case SIGXCPU: return "SIGXCPU"; +#endif +#ifdef SIGPIPE + case SIGPIPE: return "SIGPIPE"; +#endif +#ifdef SIGCHLD + case SIGCHLD: return "SIGCHLD"; +#endif +#ifdef SIGHUP + case SIGHUP: return "SIGHUP"; +#endif + default: return "UNKNOWN SIGNAL"; + } +} + +#ifdef HAVE_GRP_H + +/* Check whether the specified GID and the group name match */ +static int is_group(gid_t gid, const char *name) { + struct group group, *result = NULL; + long n; + void *data; + int r = -1; + +#ifdef HAVE_GETGRGID_R +#ifdef _SC_GETGR_R_SIZE_MAX + n = sysconf(_SC_GETGR_R_SIZE_MAX); +#else + n = -1; +#endif + if (n < 0) n = 512; + data = pa_xmalloc(n); + + if (getgrgid_r(gid, &group, data, n, &result) < 0 || !result) { + pa_log(__FILE__ ": getgrgid_r(%u) failed: %s\n", gid, strerror(errno)); + goto finish; + } + + + r = strcmp(name, result->gr_name) == 0; + +finish: + pa_xfree(data); +#else + /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) that do not + * support getgrgid_r. */ + if ((result = getgrgid(gid)) == NULL) { + pa_log(__FILE__ ": getgrgid(%u) failed: %s\n", gid, strerror(errno)); + goto finish; + } + + r = strcmp(name, result->gr_name) == 0; + +finish: +#endif + + return r; +} + +/* Check the current user is member of the specified group */ +int pa_uid_in_group(const char *name, gid_t *gid) { + GETGROUPS_T *gids, tgid; + int n = sysconf(_SC_NGROUPS_MAX); + int r = -1, i; + + assert(n > 0); + + gids = pa_xmalloc(sizeof(GETGROUPS_T)*n); + + if ((n = getgroups(n, gids)) < 0) { + pa_log(__FILE__": getgroups() failed: %s\n", strerror(errno)); + goto finish; + } + + for (i = 0; i < n; i++) { + if (is_group(gids[i], name) > 0) { + *gid = gids[i]; + r = 1; + goto finish; + } + } + + if (is_group(tgid = getgid(), name) > 0) { + *gid = tgid; + r = 1; + goto finish; + } + + r = 0; + +finish: + + pa_xfree(gids); + return r; +} + +#else /* HAVE_GRP_H */ + +int pa_uid_in_group(const char *name, gid_t *gid) { + return -1; +} + +#endif + +/* Lock or unlock a file entirely. + (advisory on UNIX, mandatory on Windows) */ +int pa_lock_fd(int fd, int b) { +#ifdef F_SETLKW + struct flock flock; + + /* Try a R/W lock first */ + + flock.l_type = b ? F_WRLCK : F_UNLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 0; + + if (fcntl(fd, F_SETLKW, &flock) >= 0) + return 0; + + /* Perhaps the file descriptor qas opened for read only, than try again with a read lock. */ + if (b && errno == EBADF) { + flock.l_type = F_RDLCK; + if (fcntl(fd, F_SETLKW, &flock) >= 0) + return 0; + } + + pa_log(__FILE__": %slock failed: %s\n", !b ? "un" : "", strerror(errno)); +#endif + +#ifdef OS_IS_WIN32 + HANDLE h = (HANDLE)_get_osfhandle(fd); + + if (b && LockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF)) + return 0; + if (!b && UnlockFile(h, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF)) + return 0; + + pa_log(__FILE__": %slock failed: 0x%08X\n", !b ? "un" : "", GetLastError()); +#endif + + return -1; +} + +/* Remove trailing newlines from a string */ +char* pa_strip_nl(char *s) { + assert(s); + + s[strcspn(s, "\r\n")] = 0; + return s; +} + +/* Create a temporary lock file and lock it. */ +int pa_lock_lockfile(const char *fn) { + int fd = -1; + assert(fn); + + for (;;) { + struct stat st; + + if ((fd = open(fn, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)) < 0) { + pa_log(__FILE__": failed to create lock file '%s': %s\n", fn, strerror(errno)); + goto fail; + } + + if (pa_lock_fd(fd, 1) < 0) { + pa_log(__FILE__": failed to lock file '%s'.\n", fn); + goto fail; + } + + if (fstat(fd, &st) < 0) { + pa_log(__FILE__": failed to fstat() file '%s'.\n", fn); + goto fail; + } + + /* Check wheter the file has been removed meanwhile. When yes, restart this loop, otherwise, we're done */ + if (st.st_nlink >= 1) + break; + + if (pa_lock_fd(fd, 0) < 0) { + pa_log(__FILE__": failed to unlock file '%s'.\n", fn); + goto fail; + } + + if (close(fd) < 0) { + pa_log(__FILE__": failed to close file '%s'.\n", fn); + goto fail; + } + + fd = -1; + } + + return fd; + +fail: + + if (fd >= 0) + close(fd); + + return -1; +} + +/* Unlock a temporary lcok file */ +int pa_unlock_lockfile(const char *fn, int fd) { + int r = 0; + assert(fn && fd >= 0); + + if (unlink(fn) < 0) { + pa_log_warn(__FILE__": WARNING: unable to remove lock file '%s': %s\n", fn, strerror(errno)); + r = -1; + } + + if (pa_lock_fd(fd, 0) < 0) { + pa_log_warn(__FILE__": WARNING: failed to unlock file '%s'.\n", fn); + r = -1; + } + + if (close(fd) < 0) { + pa_log_warn(__FILE__": WARNING: failed to close lock file '%s': %s\n", fn, strerror(errno)); + r = -1; + } + + return r; +} + +/* Try to open a configuration file. If "env" is specified, open the + * value of the specified environment variable. Otherwise look for a + * file "local" in the home directory or a file "global" in global + * file system. If "result" is non-NULL, a pointer to a newly + * allocated buffer containing the used configuration file is + * stored there.*/ +FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) { + const char *fn; + char h[PATH_MAX]; + +#ifdef OS_IS_WIN32 + char buf[PATH_MAX]; + + if (!getenv(POLYP_ROOTENV)) + pa_set_root(NULL); +#endif + + if (env && (fn = getenv(env))) { +#ifdef OS_IS_WIN32 + if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX)) + return NULL; + fn = buf; +#endif + + if (result) + *result = pa_xstrdup(fn); + + return fopen(fn, "r"); + } + + if (local && pa_get_home_dir(h, sizeof(h))) { + FILE *f; + char *lfn; + + fn = lfn = pa_sprintf_malloc("%s/%s", h, local); + +#ifdef OS_IS_WIN32 + if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) + return NULL; + fn = buf; +#endif + + f = fopen(fn, "r"); + + if (f || errno != ENOENT) { + if (result) + *result = pa_xstrdup(fn); + pa_xfree(lfn); + return f; + } + + pa_xfree(lfn); + } + + if (!global) { + if (result) + *result = NULL; + errno = ENOENT; + return NULL; + } + +#ifdef OS_IS_WIN32 + if (!ExpandEnvironmentStrings(global, buf, PATH_MAX)) + return NULL; + global = buf; +#endif + + if (result) + *result = pa_xstrdup(global); + + return fopen(global, "r"); +} + +/* Format the specified data as a hexademical string */ +char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength) { + size_t i = 0, j = 0; + const char hex[] = "0123456789abcdef"; + assert(d && s && slength > 0); + + while (i < dlength && j+3 <= slength) { + s[j++] = hex[*d >> 4]; + s[j++] = hex[*d & 0xF]; + + d++; + i++; + } + + s[j < slength ? j : slength] = 0; + return s; +} + +/* Convert a hexadecimal digit to a number or -1 if invalid */ +static int hexc(char c) { + if (c >= '0' && c <= '9') + return c - '0'; + + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + + return -1; +} + +/* Parse a hexadecimal string as created by pa_hexstr() to a BLOB */ +size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength) { + size_t j = 0; + assert(p && d); + + while (j < dlength && *p) { + int b; + + if ((b = hexc(*(p++))) < 0) + return (size_t) -1; + + d[j] = (uint8_t) (b << 4); + + if (!*p) + return (size_t) -1; + + if ((b = hexc(*(p++))) < 0) + return (size_t) -1; + + d[j] |= (uint8_t) b; + j++; + } + + return j; +} + +/* Return the fully qualified domain name in *s */ +char *pa_get_fqdn(char *s, size_t l) { + char hn[256]; +#ifdef HAVE_GETADDRINFO + struct addrinfo *a, hints; +#endif + + if (!pa_get_host_name(hn, sizeof(hn))) + return NULL; + +#ifdef HAVE_GETADDRINFO + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_CANONNAME; + + if (getaddrinfo(hn, NULL, &hints, &a) < 0 || !a || !a->ai_canonname || !*a->ai_canonname) + return pa_strlcpy(s, hn, l); + + pa_strlcpy(s, a->ai_canonname, l); + freeaddrinfo(a); + return s; +#else + return pa_strlcpy(s, hn, l); +#endif +} + +/* Returns nonzero when *s starts with *pfx */ +int pa_startswith(const char *s, const char *pfx) { + size_t l; + assert(s && pfx); + l = strlen(pfx); + + return strlen(s) >= l && strncmp(s, pfx, l) == 0; +} + +/* if fn is null return the polypaudio run time path in s (/tmp/polypaudio) + * if fn is non-null and starts with / return fn in s + * otherwise append fn to the run time path and return it in s */ +char *pa_runtime_path(const char *fn, char *s, size_t l) { + char u[256]; + +#ifndef OS_IS_WIN32 + if (fn && *fn == '/') +#else + if (fn && strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\') +#endif + return pa_strlcpy(s, fn, l); + + if (fn) + snprintf(s, l, "%s%s%c%s", PA_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PATH_SEP, fn); + else + snprintf(s, l, "%s%s", PA_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u))); + +#ifdef OS_IS_WIN32 + { + char buf[l]; + strcpy(buf, s); + ExpandEnvironmentStrings(buf, s, l); + } +#endif + + return s; +} + +/* Wait t milliseconds */ +int pa_msleep(unsigned long t) { +#ifdef OS_IS_WIN32 + Sleep(t); + return 0; +#elif defined(HAVE_NANOSLEEP) + struct timespec ts; + + ts.tv_sec = t/1000; + ts.tv_nsec = (t % 1000) * 1000000; + + return nanosleep(&ts, NULL); +#else +#error "Platform lacks a sleep function." +#endif +} + +/* Convert the string s to a signed integer in *ret_i */ +int pa_atoi(const char *s, int32_t *ret_i) { + char *x = NULL; + long l; + assert(s && ret_i); + + l = strtol(s, &x, 0); + + if (!x || *x) + return -1; + + *ret_i = (int32_t) l; + + return 0; +} + +/* Convert the string s to an unsigned integer in *ret_u */ +int pa_atou(const char *s, uint32_t *ret_u) { + char *x = NULL; + unsigned long l; + assert(s && ret_u); + + l = strtoul(s, &x, 0); + + if (!x || *x) + return -1; + + *ret_u = (uint32_t) l; + + return 0; +} diff --git a/src/polypcore/util.h b/src/polypcore/util.h new file mode 100644 index 00000000..3dc6c945 --- /dev/null +++ b/src/polypcore/util.h @@ -0,0 +1,99 @@ +#ifndef fooutilhfoo +#define fooutilhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include +#include + +#include "gccmacro.h" +#include + +struct timeval; + +void pa_make_nonblock_fd(int fd); + +int pa_make_secure_dir(const char* dir); +int pa_make_secure_parent_dir(const char *fn); + +ssize_t pa_loop_read(int fd, void*data, size_t size); +ssize_t pa_loop_write(int fd, const void*data, size_t size); + +void pa_check_signal_is_blocked(int sig); + +char *pa_sprintf_malloc(const char *format, ...) PA_GCC_PRINTF_ATTR(1,2); +char *pa_vsprintf_malloc(const char *format, va_list ap); + +char *pa_strlcpy(char *b, const char *s, size_t l); + +char *pa_get_user_name(char *s, size_t l); +char *pa_get_host_name(char *s, size_t l); +char *pa_get_fqdn(char *s, size_t l); +char *pa_get_binary_name(char *s, size_t l); +char *pa_get_home_dir(char *s, size_t l); + +char *pa_path_get_filename(const char *p); + +int pa_gettimeofday(struct timeval *tv); +pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b); +int pa_timeval_cmp(const struct timeval *a, const struct timeval *b); +pa_usec_t pa_timeval_age(const struct timeval *tv); +void pa_timeval_add(struct timeval *tv, pa_usec_t v); + +void pa_raise_priority(void); +void pa_reset_priority(void); + +int pa_fd_set_cloexec(int fd, int b); + +int pa_parse_boolean(const char *s); + +char *pa_split(const char *c, const char*delimiters, const char **state); +char *pa_split_spaces(const char *c, const char **state); + +char *pa_strip_nl(char *s); + +const char *pa_strsignal(int sig); + +int pa_uid_in_group(const char *name, gid_t *gid); + +int pa_lock_fd(int fd, int b); + +int pa_lock_lockfile(const char *fn); +int pa_unlock_lockfile(const char *fn, int fd); + +FILE *pa_open_config_file(const char *env, const char *global, const char *local, char **result); + +char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength); +size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength); + +int pa_startswith(const char *s, const char *pfx); + +char *pa_runtime_path(const char *fn, char *s, size_t l); + +int pa_msleep(unsigned long t); + +int pa_atoi(const char *s, int32_t *ret_i); +int pa_atou(const char *s, uint32_t *ret_u); + +#endif diff --git a/src/polypcore/winsock.h b/src/polypcore/winsock.h new file mode 100644 index 00000000..b1e0f7d4 --- /dev/null +++ b/src/polypcore/winsock.h @@ -0,0 +1,23 @@ +#ifndef foowinsockhfoo +#define foowinsockhfoo + +#ifdef HAVE_WINSOCK2_H +#include + +#define ESHUTDOWN WSAESHUTDOWN +#define ECONNRESET WSAECONNRESET +#define ECONNABORTED WSAECONNABORTED +#define ENETRESET WSAENETRESET +#define EINPROGRESS WSAEINPROGRESS +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#define ETIMEDOUT WSAETIMEDOUT +#define ECONNREFUSED WSAECONNREFUSED +#define EHOSTUNREACH WSAEHOSTUNREACH + +#endif + +#ifdef HAVE_WS2TCPIP_H +#include +#endif + +#endif diff --git a/src/polypcore/x11prop.c b/src/polypcore/x11prop.c new file mode 100644 index 00000000..e57fc136 --- /dev/null +++ b/src/polypcore/x11prop.c @@ -0,0 +1,70 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include "x11prop.h" + + +void pa_x11_set_prop(Display *d, const char *name, const char *data) { + Atom a = XInternAtom(d, name, False); + XChangeProperty(d, RootWindow(d, 0), a, XA_STRING, 8, PropModeReplace, (const unsigned char*) data, strlen(data)+1); +} + +void pa_x11_del_prop(Display *d, const char *name) { + Atom a = XInternAtom(d, name, False); + XDeleteProperty(d, RootWindow(d, 0), a); +} + +char* pa_x11_get_prop(Display *d, const char *name, char *p, size_t l) { + Atom actual_type; + int actual_format; + unsigned long nitems; + unsigned long nbytes_after; + unsigned char *prop = NULL; + char *ret = NULL; + + Atom a = XInternAtom(d, name, False); + if (XGetWindowProperty(d, RootWindow(d, 0), a, 0, (l+2)/4, False, XA_STRING, &actual_type, &actual_format, &nitems, &nbytes_after, &prop) != Success) + goto finish; + + if (actual_type != XA_STRING) + goto finish; + + memcpy(p, prop, nitems); + p[nitems] = 0; + + ret = p; + +finish: + + if (prop) + XFree(prop); + + return ret; +} diff --git a/src/polypcore/x11prop.h b/src/polypcore/x11prop.h new file mode 100644 index 00000000..5531c640 --- /dev/null +++ b/src/polypcore/x11prop.h @@ -0,0 +1,33 @@ +#ifndef foox11prophfoo +#define foox11prophfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include + +void pa_x11_set_prop(Display *d, const char *name, const char *data); +void pa_x11_del_prop(Display *d, const char *name); +char* pa_x11_get_prop(Display *d, const char *name, char *p, size_t l); + +#endif diff --git a/src/polypcore/x11wrap.c b/src/polypcore/x11wrap.c new file mode 100644 index 00000000..e20a50a6 --- /dev/null +++ b/src/polypcore/x11wrap.c @@ -0,0 +1,235 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +#include "llist.h" +#include "x11wrap.h" +#include "xmalloc.h" +#include "log.h" +#include "props.h" + +typedef struct pa_x11_internal pa_x11_internal; + +struct pa_x11_internal { + PA_LLIST_FIELDS(pa_x11_internal); + pa_x11_wrapper *wrapper; + pa_io_event* io_event; + int fd; +}; + +struct pa_x11_wrapper { + pa_core *core; + int ref; + + char *property_name; + Display *display; + + pa_defer_event* defer_event; + pa_io_event* io_event; + + PA_LLIST_HEAD(pa_x11_client, clients); + PA_LLIST_HEAD(pa_x11_internal, internals); +}; + +struct pa_x11_client { + PA_LLIST_FIELDS(pa_x11_client); + pa_x11_wrapper *wrapper; + int (*callback)(pa_x11_wrapper *w, XEvent *e, void *userdata); + void *userdata; +}; + +/* Dispatch all pending X11 events */ +static void work(pa_x11_wrapper *w) { + assert(w && w->ref >= 1); + + while (XPending(w->display)) { + pa_x11_client *c; + XEvent e; + XNextEvent(w->display, &e); + + for (c = w->clients; c; c = c->next) { + assert(c->callback); + if (c->callback(w, &e, c->userdata) != 0) + break; + } + } +} + +/* IO notification event for the X11 display connection */ +static void display_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { + pa_x11_wrapper *w = userdata; + assert(m && e && fd >= 0 && w && w->ref >= 1); + work(w); +} + +/* Deferred notification event. Called once each main loop iteration */ +static void defer_event(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { + pa_x11_wrapper *w = userdata; + assert(m && e && w && w->ref >= 1); + work(w); +} + +/* IO notification event for X11 internal connections */ +static void internal_io_event(pa_mainloop_api *m, pa_io_event *e, int fd, PA_GCC_UNUSED pa_io_event_flags_t f, void *userdata) { + pa_x11_wrapper *w = userdata; + assert(m && e && fd >= 0 && w && w->ref >= 1); + + XProcessInternalConnection(w->display, fd); +} + +/* Add a new IO source for the specified X11 internal connection */ +static pa_x11_internal* x11_internal_add(pa_x11_wrapper *w, int fd) { + pa_x11_internal *i; + assert(i && fd >= 0); + + i = pa_xmalloc(sizeof(pa_x11_internal)); + i->wrapper = w; + i->io_event = w->core->mainloop->io_new(w->core->mainloop, fd, PA_IO_EVENT_INPUT, internal_io_event, w); + i->fd = fd; + + PA_LLIST_PREPEND(pa_x11_internal, w->internals, i); + return i; +} + +/* Remove an IO source for an X11 internal connection */ +static void x11_internal_remove(pa_x11_wrapper *w, pa_x11_internal *i) { + assert(i); + + PA_LLIST_REMOVE(pa_x11_internal, w->internals, i); + w->core->mainloop->io_free(i->io_event); + pa_xfree(i); +} + +/* Implementation of XConnectionWatchProc */ +static void x11_watch(Display *display, XPointer userdata, int fd, Bool opening, XPointer *watch_data) { + pa_x11_wrapper *w = (pa_x11_wrapper*) userdata; + assert(display && w && fd >= 0); + + if (opening) + *watch_data = (XPointer) x11_internal_add(w, fd); + else + x11_internal_remove(w, (pa_x11_internal*) *watch_data); +} + +static pa_x11_wrapper* x11_wrapper_new(pa_core *c, const char *name, const char *t) { + pa_x11_wrapper*w; + Display *d; + int r; + + if (!(d = XOpenDisplay(name))) { + pa_log(__FILE__": XOpenDisplay() failed\n"); + return NULL; + } + + w = pa_xmalloc(sizeof(pa_x11_wrapper)); + w->core = c; + w->ref = 1; + w->property_name = pa_xstrdup(t); + w->display = d; + + PA_LLIST_HEAD_INIT(pa_x11_client, w->clients); + PA_LLIST_HEAD_INIT(pa_x11_internal, w->internals); + + w->defer_event = c->mainloop->defer_new(c->mainloop, defer_event, w); + w->io_event = c->mainloop->io_new(c->mainloop, ConnectionNumber(d), PA_IO_EVENT_INPUT, display_io_event, w); + + XAddConnectionWatch(d, x11_watch, (XPointer) w); + + r = pa_property_set(c, w->property_name, w); + assert(r >= 0); + + return w; +} + +static void x11_wrapper_free(pa_x11_wrapper*w) { + int r; + assert(w); + + r = pa_property_remove(w->core, w->property_name); + assert(r >= 0); + + assert(!w->clients); + + XRemoveConnectionWatch(w->display, x11_watch, (XPointer) w); + XCloseDisplay(w->display); + + w->core->mainloop->io_free(w->io_event); + w->core->mainloop->defer_free(w->defer_event); + + while (w->internals) + x11_internal_remove(w, w->internals); + + pa_xfree(w->property_name); + pa_xfree(w); +} + +pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name) { + char t[256]; + pa_x11_wrapper *w; + assert(c); + + snprintf(t, sizeof(t), "x11-wrapper%s%s", name ? "-" : "", name ? name : ""); + if ((w = pa_property_get(c, t))) + return pa_x11_wrapper_ref(w); + + return x11_wrapper_new(c, name, t); +} + +pa_x11_wrapper* pa_x11_wrapper_ref(pa_x11_wrapper *w) { + assert(w && w->ref >= 1); + w->ref++; + return w; +} + +void pa_x11_wrapper_unref(pa_x11_wrapper* w) { + assert(w && w->ref >= 1); + + if (!(--w->ref)) + x11_wrapper_free(w); +} + +Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w) { + assert(w && w->ref >= 1); + return w->display; +} + +pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w, XEvent *e, void *userdata), void *userdata) { + pa_x11_client *c; + assert(w && w->ref >= 1); + + c = pa_xmalloc(sizeof(pa_x11_client)); + c->wrapper = w; + c->callback = cb; + c->userdata = userdata; + + PA_LLIST_PREPEND(pa_x11_client, w->clients, c); + + return c; +} + +void pa_x11_client_free(pa_x11_client *c) { + assert(c && c->wrapper && c->wrapper->ref >= 1); + + PA_LLIST_REMOVE(pa_x11_client, c->wrapper->clients, c); + pa_xfree(c); +} diff --git a/src/polypcore/x11wrap.h b/src/polypcore/x11wrap.h new file mode 100644 index 00000000..15645168 --- /dev/null +++ b/src/polypcore/x11wrap.h @@ -0,0 +1,52 @@ +#ifndef foox11wraphfoo +#define foox11wraphfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "core.h" + +typedef struct pa_x11_wrapper pa_x11_wrapper; + +/* Return the X11 wrapper for this core. In case no wrapper was + existant before, allocate a new one */ +pa_x11_wrapper* pa_x11_wrapper_get(pa_core *c, const char *name); + +/* Increase the wrapper's reference count by one */ +pa_x11_wrapper* pa_x11_wrapper_ref(pa_x11_wrapper *w); + +/* Decrease the reference counter of an X11 wrapper object */ +void pa_x11_wrapper_unref(pa_x11_wrapper* w); + +/* Return the X11 display object for this connection */ +Display *pa_x11_wrapper_get_display(pa_x11_wrapper *w); + +typedef struct pa_x11_client pa_x11_client; + +/* Register an X11 client, that is called for each X11 event */ +pa_x11_client* pa_x11_client_new(pa_x11_wrapper *w, int (*cb)(pa_x11_wrapper *w, XEvent *e, void *userdata), void *userdata); + +/* Free an X11 client object */ +void pa_x11_client_free(pa_x11_client *c); + +#endif diff --git a/src/polypcore/xmalloc.c b/src/polypcore/xmalloc.c new file mode 100644 index 00000000..bf366347 --- /dev/null +++ b/src/polypcore/xmalloc.c @@ -0,0 +1,122 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "memory.h" +#include "util.h" +#include "xmalloc.h" +#include "gccmacro.h" + +/* Make sure not to allocate more than this much memory. */ +#define MAX_ALLOC_SIZE (1024*1024*20) /* 20MB */ + +/* #undef malloc */ +/* #undef free */ +/* #undef realloc */ +/* #undef strndup */ +/* #undef strdup */ + +static void oom(void) PA_GCC_NORETURN; + +/** called in case of an OOM situation. Prints an error message and + * exits */ +static void oom(void) { + static const char e[] = "Not enough memory\n"; + pa_loop_write(STDERR_FILENO, e, sizeof(e)-1); +#ifdef SIGQUIT + raise(SIGQUIT); +#endif + _exit(1); +} + +void* pa_xmalloc(size_t size) { + void *p; + assert(size > 0); + assert(size < MAX_ALLOC_SIZE); + + if (!(p = malloc(size))) + oom(); + + return p; +} + +void* pa_xmalloc0(size_t size) { + void *p; + assert(size > 0); + assert(size < MAX_ALLOC_SIZE); + + if (!(p = calloc(1, size))) + oom(); + + return p; +} + +void *pa_xrealloc(void *ptr, size_t size) { + void *p; + assert(size > 0); + assert(size < MAX_ALLOC_SIZE); + + if (!(p = realloc(ptr, size))) + oom(); + return p; +} + +void* pa_xmemdup(const void *p, size_t l) { + if (!p) + return NULL; + else { + char *r = pa_xmalloc(l); + memcpy(r, p, l); + return r; + } +} + +char *pa_xstrdup(const char *s) { + if (!s) + return NULL; + + return pa_xmemdup(s, strlen(s)+1); +} + +char *pa_xstrndup(const char *s, size_t l) { + if (!s) + return NULL; + else { + char *r; + size_t t = strlen(s); + + if (t > l) + t = l; + + r = pa_xmemdup(s, t+1); + r[t] = 0; + return r; + } +} + diff --git a/src/polypcore/xmalloc.h b/src/polypcore/xmalloc.h new file mode 100644 index 00000000..2946011a --- /dev/null +++ b/src/polypcore/xmalloc.h @@ -0,0 +1,58 @@ +#ifndef foomemoryhfoo +#define foomemoryhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include +#include +#include + +void* pa_xmalloc(size_t l); +void *pa_xmalloc0(size_t l); +void *pa_xrealloc(void *ptr, size_t size); +#define pa_xfree free + +char *pa_xstrdup(const char *s); +char *pa_xstrndup(const char *s, size_t l); + +void* pa_xmemdup(const void *p, size_t l); + +/** Internal helper for pa_xnew() */ +static inline void* pa_xnew_internal(unsigned n, size_t k) { + assert(n < INT_MAX/k); + return pa_xmalloc(n*k); +} + +/** Allocate n new structures of the specified type. */ +#define pa_xnew(type, n) ((type*) pa_xnew_internal((n), sizeof(type))) + +/** Internal helper for pa_xnew0() */ +static inline void* pa_xnew0_internal(unsigned n, size_t k) { + assert(n < INT_MAX/k); + return pa_xmalloc0(n*k); +} + +/** Same as pa_xnew() but set the memory to zero */ +#define pa_xnew0(type, n) ((type*) pa_xnew0_internal((n), sizeof(type))) + +#endif diff --git a/src/tests/cpulimit-test.c b/src/tests/cpulimit-test.c new file mode 100644 index 00000000..97a8a0dd --- /dev/null +++ b/src/tests/cpulimit-test.c @@ -0,0 +1,92 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "../daemon/cpulimit.h" +#include +#include + +#ifdef TEST2 +#include +#endif + +/* A simple example for testing the cpulimit subsystem */ + +static time_t start; + +#ifdef TEST2 + +static void func(pa_mainloop_api *m, PA_GCC_UNUSED pa_signal_event *e, PA_GCC_UNUSED int sig, PA_GCC_UNUSED void *userdata) { + time_t now; + time(&now); + + if ((now - start) >= 30) { + m->quit(m, 1); + fprintf(stderr, "Test failed\n"); + } else + raise(SIGUSR1); +} + +#endif + +int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { + pa_mainloop *m; + + m = pa_mainloop_new(); + assert(m); + + pa_cpu_limit_init(pa_mainloop_get_api(m)); + + time(&start); + +#ifdef TEST2 + pa_signal_init(pa_mainloop_get_api(m)); + pa_signal_new(SIGUSR1, func, NULL); + raise(SIGUSR1); + pa_mainloop_run(m, NULL); + pa_signal_done(); +#else + for (;;) { + time_t now; + time(&now); + + if ((now - start) >= 30) { + fprintf(stderr, "Test failed\n"); + break; + } + } +#endif + + pa_cpu_limit_done(); + + pa_mainloop_free(m); + + return 0; +} diff --git a/src/tests/mainloop-test.c b/src/tests/mainloop-test.c new file mode 100644 index 00000000..f62c9693 --- /dev/null +++ b/src/tests/mainloop-test.c @@ -0,0 +1,146 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include + +#ifdef GLIB_MAIN_LOOP + +#include +#include + +static GMainLoop* glib_main_loop = NULL; + +#if GLIB_MAJOR_VERSION >= 2 +#define GLIB20 +#else +#undef GLIB20 +#endif + + +#else /* GLIB_MAIN_LOOP */ +#include +#endif /* GLIB_MAIN_LOOP */ + +static pa_defer_event *de; + +static void iocb(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) { + unsigned char c; + read(fd, &c, sizeof(c)); + fprintf(stderr, "IO EVENT: %c\n", c < 32 ? '.' : c); + a->defer_enable(de, 1); +} + +static void dcb(pa_mainloop_api*a, pa_defer_event *e, void *userdata) { + fprintf(stderr, "DEFER EVENT\n"); + a->defer_enable(e, 0); +} + +static void tcb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) { + fprintf(stderr, "TIME EVENT\n"); + +#if defined(GLIB_MAIN_LOOP) && defined(GLIB20) + g_main_loop_quit(glib_main_loop); +#elif defined(GLIB_MAIN_LOOP) + g_main_quit(glib_main_loop); +#else + a->quit(a, 0); +#endif +} + +int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { + pa_mainloop_api *a; + pa_io_event *ioe; + pa_time_event *te; + struct timeval tv; + +#ifdef GLIB_MAIN_LOOP + pa_glib_mainloop *g; + +#ifdef GLIB20 + glib_main_loop = g_main_loop_new(NULL, FALSE); + assert(glib_main_loop); + + g = pa_glib_mainloop_new(NULL); +#else /* GLIB20 */ + glib_main_loop = g_main_new(FALSE); + assert(glib_main_loop); + + g = pa_glib_mainloop_new(); +#endif /* GLIB20 */ + assert(g); + + a = pa_glib_mainloop_get_api(g); + assert(a); +#else /* GLIB_MAIN_LOOP */ + pa_mainloop *m; + + m = pa_mainloop_new(); + assert(m); + + a = pa_mainloop_get_api(m); + assert(a); +#endif /* GLIB_MAIN_LOOP */ + + ioe = a->io_new(a, 0, PA_IO_EVENT_INPUT, iocb, NULL); + assert(ioe); + + de = a->defer_new(a, dcb, NULL); + assert(de); + + pa_gettimeofday(&tv); + tv.tv_sec += 10; + te = a->time_new(a, &tv, tcb, NULL); + +#if defined(GLIB_MAIN_LOOP) && defined(GLIB20) + g_main_loop_run(glib_main_loop); +#elif defined(GLIB_MAIN_LOOP) + g_main_run(glib_main_loop); +#else + pa_mainloop_run(m, NULL); +#endif + + a->time_free(te); + a->defer_free(de); + a->io_free(ioe); + +#ifdef GLIB_MAIN_LOOP + pa_glib_mainloop_free(g); +#ifdef GLIB20 + g_main_loop_unref(glib_main_loop); +#else + g_main_destroy(glib_main_loop); +#endif +#else + pa_mainloop_free(m); +#endif + + return 0; +} diff --git a/src/tests/mcalign-test.c b/src/tests/mcalign-test.c new file mode 100644 index 00000000..861c38c1 --- /dev/null +++ b/src/tests/mcalign-test.c @@ -0,0 +1,96 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* A simple program for testing pa_mcalign */ + +int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { + pa_mcalign *a = pa_mcalign_new(11, NULL); + pa_memchunk c; + + pa_memchunk_reset(&c); + + srand(time(NULL)); + + for (;;) { + ssize_t r; + size_t l; + + if (!c.memblock) { + c.memblock = pa_memblock_new(2048, NULL); + c.index = c.length = 0; + } + + assert(c.index < c.memblock->length); + + l = c.memblock->length - c.index; + + l = l <= 1 ? l : rand() % (l-1) +1 ; + + if ((r = read(STDIN_FILENO, (uint8_t*) c.memblock->data + c.index, l)) <= 0) { + fprintf(stderr, "read() failed: %s\n", r < 0 ? strerror(errno) : "EOF"); + break; + } + + c.length = r; + pa_mcalign_push(a, &c); + fprintf(stderr, "Read %d bytes\n", r); + + c.index += r; + + if (c.index >= c.memblock->length) { + pa_memblock_unref(c.memblock); + pa_memchunk_reset(&c); + } + + for (;;) { + pa_memchunk t; + + if (pa_mcalign_pop(a, &t) < 0) + break; + + pa_loop_write(STDOUT_FILENO, (uint8_t*) t.memblock->data + t.index, t.length); + fprintf(stderr, "Wrote %lu bytes.\n", (unsigned long) t.length); + + pa_memblock_unref(t.memblock); + } + } + + pa_mcalign_free(a); + + if (c.memblock) + pa_memblock_unref(c.memblock); +} diff --git a/src/tests/pacat-simple.c b/src/tests/pacat-simple.c new file mode 100644 index 00000000..8b3a7b22 --- /dev/null +++ b/src/tests/pacat-simple.c @@ -0,0 +1,101 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#define BUFSIZE 1024 + +int main(PA_GCC_UNUSED int argc, char*argv[]) { + + /* The Sample format to use */ + static const pa_sample_spec ss = { + .format = PA_SAMPLE_S16LE, + .rate = 44100, + .channels = 2 + }; + + pa_simple *s = NULL; + int ret = 1; + int error; + + /* Create a new playback stream */ + if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, &error))) { + fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); + goto finish; + } + + for (;;) { + uint8_t buf[BUFSIZE]; + ssize_t r; + +#if 0 + pa_usec_t latency; + + if ((latency = pa_simple_get_playback_latency(s, &error)) == (pa_usec_t) -1) { + fprintf(stderr, __FILE__": pa_simple_get_playback_latency() failed: %s\n", pa_strerror(error)); + goto finish; + } + + fprintf(stderr, "%0.0f usec \r", (float)latency); +#endif + + /* Read some data ... */ + if ((r = read(STDIN_FILENO, buf, sizeof(buf))) <= 0) { + if (r == 0) /* EOF */ + break; + + fprintf(stderr, __FILE__": read() failed: %s\n", strerror(errno)); + goto finish; + } + + /* ... and play it */ + if (pa_simple_write(s, buf, r, &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error)); + goto finish; + } + } + + /* Make sure that every single sample was played */ + if (pa_simple_drain(s, &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error)); + goto finish; + } + + ret = 0; + +finish: + + if (s) + pa_simple_free(s); + + return ret; +} diff --git a/src/tests/parec-simple.c b/src/tests/parec-simple.c new file mode 100644 index 00000000..10eaea8d --- /dev/null +++ b/src/tests/parec-simple.c @@ -0,0 +1,100 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#define BUFSIZE 1024 + +/* A simple routine calling UNIX write() in a loop */ +static ssize_t loop_write(int fd, const void*data, size_t size) { + ssize_t ret = 0; + + while (size > 0) { + ssize_t r; + + if ((r = write(fd, data, size)) < 0) + return r; + + if (r == 0) + break; + + ret += r; + data = (const uint8_t*) data + r; + size -= r; + } + + return ret; +} + +int main(PA_GCC_UNUSED int argc, char*argv[]) { + /* The sample type to use */ + static const pa_sample_spec ss = { + .format = PA_SAMPLE_S16LE, + .rate = 44100, + .channels = 2 + }; + pa_simple *s = NULL; + int ret = 1; + int error; + + /* Create the recording stream */ + if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "record", &ss, NULL, &error))) { + fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); + goto finish; + } + + for (;;) { + uint8_t buf[BUFSIZE]; + ssize_t r; + + /* Record some data ... */ + if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error)); + goto finish; + } + + /* And write it to STDOUT */ + if ((r = loop_write(STDOUT_FILENO, buf, sizeof(buf))) <= 0) { + fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno)); + goto finish; + } + } + + ret = 0; + +finish: + + if (s) + pa_simple_free(s); + + return ret; +} diff --git a/src/tests/strlist-test.c b/src/tests/strlist-test.c new file mode 100644 index 00000000..14543112 --- /dev/null +++ b/src/tests/strlist-test.c @@ -0,0 +1,42 @@ +#include + +#include +#include +#include + +int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char* argv[]) { + char *t, *u; + pa_strlist *l = NULL; + + l = pa_strlist_prepend(l, "e"); + l = pa_strlist_prepend(l, "d"); + l = pa_strlist_prepend(l, "c"); + l = pa_strlist_prepend(l, "b"); + l = pa_strlist_prepend(l, "a"); + + t = pa_strlist_tostring(l); + pa_strlist_free(l); + + fprintf(stderr, "1: %s\n", t); + + l = pa_strlist_parse(t); + pa_xfree(t); + + t = pa_strlist_tostring(l); + fprintf(stderr, "2: %s\n", t); + pa_xfree(t); + + l = pa_strlist_pop(l, &u); + fprintf(stderr, "3: %s\n", u); + pa_xfree(u); + + l = pa_strlist_remove(l, "c"); + + t = pa_strlist_tostring(l); + fprintf(stderr, "4: %s\n", t); + pa_xfree(t); + + pa_strlist_free(l); + + return 0; +} diff --git a/src/tests/voltest.c b/src/tests/voltest.c new file mode 100644 index 00000000..58f1da00 --- /dev/null +++ b/src/tests/voltest.c @@ -0,0 +1,20 @@ +/* $Id$ */ + +#include + +#include +#include + +int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { + pa_volume_t v; + + for (v = PA_VOLUME_MUTED; v <= PA_VOLUME_NORM*2; v += 256) { + + double dB = pa_sw_volume_to_dB(v); + double f = pa_sw_volume_to_linear(v); + + printf("Volume: %3i; percent: %i%%; decibel %0.2f; linear = %0.2f; volume(decibel): %3i; volume(linear): %3i\n", + v, (v*100)/PA_VOLUME_NORM, dB, f, pa_sw_volume_from_dB(dB), pa_sw_volume_from_linear(f)); + + } +} diff --git a/src/utils/esdcompat.sh.in b/src/utils/esdcompat.sh.in new file mode 100755 index 00000000..76023f52 --- /dev/null +++ b/src/utils/esdcompat.sh.in @@ -0,0 +1,98 @@ +#!/bin/sh + +# $Id$ +# +# This file is part of polypaudio. +# +# polypaudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# polypaudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with polypaudio; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + +VERSION_STRING="@PACKAGE_NAME@ esd wrapper @PACKAGE_VERSION@" + +fail() { + echo "ERROR: $1" + exit 1 +} + +ARGS=" --log-target=syslog" + +for N in $(seq $#) ; do + + case "$1" in + "") + ;; + + -v|--version) + echo "$VERSION_STRING" + exit 0 + ;; + + -h|--help) + cat < +#endif + +#include +#include +#include + +#include +#include +#include +#include + +static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) { + fprintf(stderr, "Got signal, exiting\n"); + m->quit(m, 0); +} + +static void dump_server(const pa_browse_info *i) { + char t[16]; + + if (i->cookie) + snprintf(t, sizeof(t), "0x%08x", *i->cookie); + + printf("server: %s\n" + "server-version: %s\n" + "user-name: %s\n" + "fqdn: %s\n" + "cookie: %s\n", + i->server, + i->server_version ? i->server_version : "n/a", + i->user_name ? i->user_name : "n/a", + i->fqdn ? i->fqdn : "n/a", + i->cookie ? t : "n/a"); +} + +static void dump_device(const pa_browse_info *i) { + char t[16], ss[PA_SAMPLE_SPEC_SNPRINT_MAX]; + + if (i->sample_spec) + pa_sample_spec_snprint(ss, sizeof(ss), i->sample_spec); + + if (i->typeid) + pa_typeid_to_string(*i->typeid, t, sizeof(t)); + + printf("device: %s\n" + "description: %s\n" + "type: %s\n" + "sample spec: %s\n", + i->device, + i->description ? i->description : "n/a", + i->typeid ? t : "n/a", + i->sample_spec ? ss : "n/a"); + +} + +static void browser_callback(pa_browser *b, pa_browse_opcode c, const pa_browse_info *i, void *userdata) { + assert(b && i); + + switch (c) { + + case PA_BROWSE_NEW_SERVER: + printf("\n=> new server <%s>\n", i->name); + dump_server(i); + break; + + case PA_BROWSE_NEW_SINK: + printf("\n=> new sink <%s>\n", i->name); + dump_server(i); + dump_device(i); + break; + + case PA_BROWSE_NEW_SOURCE: + printf("\n=> new source <%s>\n", i->name); + dump_server(i); + dump_device(i); + break; + + case PA_BROWSE_REMOVE: + printf("\n=> removed service <%s>\n", i->name); + break; + + default: + ; + } +} + + +int main(int argc, char *argv[]) { + pa_mainloop *mainloop = NULL; + pa_browser *browser = NULL; + int ret = 1, r; + + if (!(mainloop = pa_mainloop_new())) + goto finish; + + r = pa_signal_init(pa_mainloop_get_api(mainloop)); + assert(r == 0); + pa_signal_new(SIGINT, exit_signal_callback, NULL); + pa_signal_new(SIGTERM, exit_signal_callback, NULL); + signal(SIGPIPE, SIG_IGN); + + if (!(browser = pa_browser_new(pa_mainloop_get_api(mainloop)))) + goto finish; + + pa_browser_set_callback(browser, browser_callback, NULL); + + ret = 0; + pa_mainloop_run(mainloop, &ret); + +finish: + if (mainloop) { + pa_signal_done(); + pa_mainloop_free(mainloop); + } + + return ret; +} diff --git a/src/utils/pacat.c b/src/utils/pacat.c new file mode 100644 index 00000000..bd2b64fd --- /dev/null +++ b/src/utils/pacat.c @@ -0,0 +1,541 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if PA_API_VERSION != 8 +#error Invalid Polypaudio API version +#endif + +static enum { RECORD, PLAYBACK } mode = PLAYBACK; + +static pa_context *context = NULL; +static pa_stream *stream = NULL; +static pa_mainloop_api *mainloop_api = NULL; + +static void *buffer = NULL; +static size_t buffer_length = 0, buffer_index = 0; + +static pa_io_event* stdio_event = NULL; + +static char *stream_name = NULL, *client_name = NULL, *device = NULL; + +static int verbose = 0; +static pa_volume_t volume = PA_VOLUME_NORM; + +static pa_sample_spec sample_spec = { + .format = PA_SAMPLE_S16LE, + .rate = 44100, + .channels = 2 +}; + +/* A shortcut for terminating the application */ +static void quit(int ret) { + assert(mainloop_api); + mainloop_api->quit(mainloop_api, ret); +} + +/* Write some data to the stream */ +static void do_stream_write(size_t length) { + size_t l; + assert(length); + + if (!buffer || !buffer_length) + return; + + l = length; + if (l > buffer_length) + l = buffer_length; + + pa_stream_write(stream, (uint8_t*) buffer + buffer_index, l, NULL, 0); + buffer_length -= l; + buffer_index += l; + + if (!buffer_length) { + free(buffer); + buffer = NULL; + buffer_index = buffer_length = 0; + } +} + +/* This is called whenever new data may be written to the stream */ +static void stream_write_callback(pa_stream *s, size_t length, void *userdata) { + assert(s && length); + + if (stdio_event) + mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT); + + if (!buffer) + return; + + do_stream_write(length); +} + +/* This is called whenever new data may is available */ +static void stream_read_callback(pa_stream *s, const void*data, size_t length, void *userdata) { + assert(s && data && length); + + if (stdio_event) + mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT); + + if (buffer) { + fprintf(stderr, "Buffer overrun, dropping incoming data\n"); + return; + } + + buffer = malloc(buffer_length = length); + assert(buffer); + memcpy(buffer, data, length); + buffer_index = 0; +} + +/* This routine is called whenever the stream state changes */ +static void stream_state_callback(pa_stream *s, void *userdata) { + assert(s); + + switch (pa_stream_get_state(s)) { + case PA_STREAM_CREATING: + case PA_STREAM_TERMINATED: + break; + + case PA_STREAM_READY: + if (verbose) + fprintf(stderr, "Stream successfully created\n"); + break; + + case PA_STREAM_FAILED: + default: + fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s)))); + quit(1); + } +} + +/* This is called whenever the context status changes */ +static void context_state_callback(pa_context *c, void *userdata) { + assert(c); + + switch (pa_context_get_state(c)) { + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + + case PA_CONTEXT_READY: + + assert(c && !stream); + + if (verbose) + fprintf(stderr, "Connection established.\n"); + + stream = pa_stream_new(c, stream_name, &sample_spec, NULL); + assert(stream); + + pa_stream_set_state_callback(stream, stream_state_callback, NULL); + pa_stream_set_write_callback(stream, stream_write_callback, NULL); + pa_stream_set_read_callback(stream, stream_read_callback, NULL); + + if (mode == PLAYBACK) { + pa_cvolume cv; + pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, PA_CHANNELS_MAX, volume)); + } else + pa_stream_connect_record(stream, device, NULL, 0); + + break; + + case PA_CONTEXT_TERMINATED: + quit(0); + break; + + case PA_CONTEXT_FAILED: + default: + fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + } +} + +/* Connection draining complete */ +static void context_drain_complete(pa_context*c, void *userdata) { + pa_context_disconnect(c); +} + +/* Stream draining complete */ +static void stream_drain_complete(pa_stream*s, int success, void *userdata) { + pa_operation *o; + + if (!success) { + fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context))); + quit(1); + } + + if (verbose) + fprintf(stderr, "Playback stream drained.\n"); + + pa_stream_disconnect(stream); + pa_stream_unref(stream); + stream = NULL; + + if (!(o = pa_context_drain(context, context_drain_complete, NULL))) + pa_context_disconnect(context); + else { + pa_operation_unref(o); + + if (verbose) + fprintf(stderr, "Draining connection to server.\n"); + } +} + +/* New data on STDIN **/ +static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) { + size_t l, w = 0; + ssize_t r; + assert(a == mainloop_api && e && stdio_event == e); + + if (buffer) { + mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL); + return; + } + + if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY || !(l = w = pa_stream_writable_size(stream))) + l = 4096; + + buffer = malloc(l); + assert(buffer); + if ((r = read(fd, buffer, l)) <= 0) { + if (r == 0) { + if (verbose) + fprintf(stderr, "Got EOF.\n"); + pa_operation_unref(pa_stream_drain(stream, stream_drain_complete, NULL)); + } else { + fprintf(stderr, "read() failed: %s\n", strerror(errno)); + quit(1); + } + + mainloop_api->io_free(stdio_event); + stdio_event = NULL; + return; + } + + buffer_length = r; + buffer_index = 0; + + if (w) + do_stream_write(w); +} + +/* Some data may be written to STDOUT */ +static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) { + ssize_t r; + assert(a == mainloop_api && e && stdio_event == e); + + if (!buffer) { + mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL); + return; + } + + assert(buffer_length); + + if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) { + fprintf(stderr, "write() failed: %s\n", strerror(errno)); + quit(1); + + mainloop_api->io_free(stdio_event); + stdio_event = NULL; + return; + } + + buffer_length -= r; + buffer_index += r; + + if (!buffer_length) { + free(buffer); + buffer = NULL; + buffer_length = buffer_index = 0; + } +} + +/* UNIX signal to quit recieved */ +static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) { + if (verbose) + fprintf(stderr, "Got signal, exiting.\n"); + quit(0); + +} + +/* Show the current latency */ +static void stream_get_latency_callback(pa_stream *s, const pa_latency_info *i, void *userdata) { + pa_usec_t total; + int negative = 0; + assert(s); + + if (!i) { + fprintf(stderr, "Failed to get latency: %s\n", pa_strerror(pa_context_errno(context))); + quit(1); + return; + } + + total = pa_stream_get_latency(s, i, &negative); + + fprintf(stderr, "Latency: buffer: %0.0f usec; sink: %0.0f usec; source: %0.0f usec; transport: %0.0f usec; total: %0.0f usec; synchronized clocks: %s.\n", + (float) i->buffer_usec, (float) i->sink_usec, (float) i->source_usec, (float) i->transport_usec, (float) total * (negative?-1:1), + i->synchronized_clocks ? "yes" : "no"); +} + +/* Someone requested that the latency is shown */ +static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) { + fprintf(stderr, "Got SIGUSR1, requesting latency.\n"); + pa_operation_unref(pa_stream_get_latency_info(stream, stream_get_latency_callback, NULL)); +} + + +static void help(const char *argv0) { + + printf("%s [options]\n\n" + " -h, --help Show this help\n" + " --version Show version\n\n" + " -r, --record Create a connection for recording\n" + " -p, --playback Create a connection for playback\n\n" + " -v, --verbose Enable verbose operations\n\n" + " -s, --server=SERVER The name of the server to connect to\n" + " -d, --device=DEVICE The name of the sink/source to connect to\n" + " -n, --client-name=NAME How to call this client on the server\n" + " --stream-name=NAME How to call this stream on the server\n" + " --volume=VOLUME Specify the initial (linear) volume in range 0...256\n" + " --rate=SAMPLERATE The sample rate in Hz (defaults to 44100)\n" + " --format=SAMPLEFORMAT The sample type, one of s16le, s16be, u8, float32le,\n" + " float32be, ulaw, alaw (defaults to s16ne)\n" + " --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n" + " (defaults to 2)\n", + argv0); +} + +enum { + ARG_VERSION = 256, + ARG_STREAM_NAME, + ARG_VOLUME, + ARG_SAMPLERATE, + ARG_SAMPLEFORMAT, + ARG_CHANNELS +}; + +int main(int argc, char *argv[]) { + pa_mainloop* m = NULL; + int ret = 1, r, c; + char *bn, *server = NULL; + + static const struct option long_options[] = { + {"record", 0, NULL, 'r'}, + {"playback", 0, NULL, 'p'}, + {"device", 1, NULL, 'd'}, + {"server", 1, NULL, 's'}, + {"client-name", 1, NULL, 'n'}, + {"stream-name", 1, NULL, ARG_STREAM_NAME}, + {"version", 0, NULL, ARG_VERSION}, + {"help", 0, NULL, 'h'}, + {"verbose", 0, NULL, 'v'}, + {"volume", 1, NULL, ARG_VOLUME}, + {"rate", 1, NULL, ARG_SAMPLERATE}, + {"format", 1, NULL, ARG_SAMPLEFORMAT}, + {"channels", 1, NULL, ARG_CHANNELS}, + {NULL, 0, NULL, 0} + }; + + if (!(bn = strrchr(argv[0], '/'))) + bn = argv[0]; + else + bn++; + + if (strstr(bn, "rec") || strstr(bn, "mon")) + mode = RECORD; + else if (strstr(bn, "cat") || strstr(bn, "play")) + mode = PLAYBACK; + + while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) { + + switch (c) { + case 'h' : + help(bn); + ret = 0; + goto quit; + + case ARG_VERSION: + printf("pacat "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version()); + ret = 0; + goto quit; + + case 'r': + mode = RECORD; + break; + + case 'p': + mode = PLAYBACK; + break; + + case 'd': + free(device); + device = strdup(optarg); + break; + + case 's': + free(server); + server = strdup(optarg); + break; + + case 'n': + free(client_name); + client_name = strdup(optarg); + break; + + case ARG_STREAM_NAME: + free(stream_name); + stream_name = strdup(optarg); + break; + + case 'v': + verbose = 1; + break; + + case ARG_VOLUME: { + int v = atoi(optarg); + volume = v < 0 ? 0 : v; + break; + } + + case ARG_CHANNELS: + sample_spec.channels = atoi(optarg); + break; + + case ARG_SAMPLEFORMAT: + sample_spec.format = pa_parse_sample_format(optarg); + break; + + case ARG_SAMPLERATE: + sample_spec.rate = atoi(optarg); + break; + + default: + goto quit; + } + } + + if (!client_name) + client_name = strdup(bn); + + if (!stream_name) + stream_name = strdup(client_name); + + if (!pa_sample_spec_valid(&sample_spec)) { + fprintf(stderr, "Invalid sample specification\n"); + goto quit; + } + + if (verbose) { + char t[PA_SAMPLE_SPEC_SNPRINT_MAX]; + pa_sample_spec_snprint(t, sizeof(t), &sample_spec); + fprintf(stderr, "Opening a %s stream with sample specification '%s'.\n", mode == RECORD ? "recording" : "playback", t); + } + + /* Set up a new main loop */ + if (!(m = pa_mainloop_new())) { + fprintf(stderr, "pa_mainloop_new() failed.\n"); + goto quit; + } + + mainloop_api = pa_mainloop_get_api(m); + + r = pa_signal_init(mainloop_api); + assert(r == 0); + pa_signal_new(SIGINT, exit_signal_callback, NULL); + pa_signal_new(SIGTERM, exit_signal_callback, NULL); +#ifdef SIGUSR1 + pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL); +#endif +#ifdef SIGPIPE + signal(SIGPIPE, SIG_IGN); +#endif + + if (!(stdio_event = mainloop_api->io_new(mainloop_api, + mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO, + mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT, + mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) { + fprintf(stderr, "source_io() failed.\n"); + goto quit; + } + + /* Create a new connection context */ + if (!(context = pa_context_new(mainloop_api, client_name))) { + fprintf(stderr, "pa_context_new() failed.\n"); + goto quit; + } + + pa_context_set_state_callback(context, context_state_callback, NULL); + + /* Connect the context */ + pa_context_connect(context, server, 1, NULL); + + /* Run the main loop */ + if (pa_mainloop_run(m, &ret) < 0) { + fprintf(stderr, "pa_mainloop_run() failed.\n"); + goto quit; + } + +quit: + if (stream) + pa_stream_unref(stream); + + if (context) + pa_context_unref(context); + + if (stdio_event) { + assert(mainloop_api); + mainloop_api->io_free(stdio_event); + } + + if (m) { + pa_signal_done(); + pa_mainloop_free(m); + } + + free(buffer); + + free(server); + free(device); + free(client_name); + free(stream_name); + + return ret; +} diff --git a/src/utils/pacmd.c b/src/utils/pacmd.c new file mode 100644 index 00000000..4054bc2c --- /dev/null +++ b/src/utils/pacmd.c @@ -0,0 +1,183 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +int main(PA_GCC_UNUSED int main, PA_GCC_UNUSED char*argv[]) { + pid_t pid ; + int fd = -1; + int ret = 1, i; + struct sockaddr_un sa; + char ibuf[256], obuf[256]; + size_t ibuf_index, ibuf_length, obuf_index, obuf_length; + fd_set ifds, ofds; + + if (pa_pid_file_check_running(&pid) < 0) { + pa_log(__FILE__": no Polypaudio daemon running\n"); + goto fail; + } + + if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { + pa_log(__FILE__": socket(PF_UNIX, SOCK_STREAM, 0): %s\n", strerror(errno)); + goto fail; + } + + memset(&sa, 0, sizeof(sa)); + sa.sun_family = AF_UNIX; + pa_runtime_path("cli", sa.sun_path, sizeof(sa.sun_path)); + + for (i = 0; i < 5; i++) { + int r; + + if ((r = connect(fd, (struct sockaddr*) &sa, sizeof(sa))) < 0 && (errno != ECONNREFUSED && errno != ENOENT)) { + pa_log(__FILE__": connect() failed: %s\n", strerror(errno)); + goto fail; + } + + if (r >= 0) + break; + + if (pa_pid_file_kill(SIGUSR2, NULL) < 0) { + pa_log(__FILE__": failed to kill Polypaudio daemon.\n"); + goto fail; + } + + pa_msleep(50); + } + + if (i >= 5) { + pa_log(__FILE__": daemon not responding.\n"); + goto fail; + } + + ibuf_index = ibuf_length = obuf_index = obuf_length = 0; + + + FD_ZERO(&ifds); + FD_SET(0, &ifds); + FD_SET(fd, &ifds); + + FD_ZERO(&ofds); + + for (;;) { + if (select(FD_SETSIZE, &ifds, &ofds, NULL, NULL) < 0) { + pa_log(__FILE__": select() failed: %s\n", strerror(errno)); + goto fail; + } + + if (FD_ISSET(0, &ifds)) { + ssize_t r; + assert(!ibuf_length); + + if ((r = read(0, ibuf, sizeof(ibuf))) <= 0) { + if (r == 0) + break; + + pa_log(__FILE__": read() failed: %s\n", strerror(errno)); + goto fail; + } + + ibuf_length = (size_t) r; + ibuf_index = 0; + } + + if (FD_ISSET(fd, &ifds)) { + ssize_t r; + assert(!obuf_length); + + if ((r = read(fd, obuf, sizeof(obuf))) <= 0) { + if (r == 0) + break; + + pa_log(__FILE__": read() failed: %s\n", strerror(errno)); + goto fail; + } + + obuf_length = (size_t) r; + obuf_index = 0; + } + + if (FD_ISSET(1, &ofds)) { + ssize_t r; + assert(obuf_length); + + if ((r = write(1, obuf + obuf_index, obuf_length)) < 0) { + pa_log(__FILE__": write() failed: %s\n", strerror(errno)); + goto fail; + } + + obuf_length -= (size_t) r; + obuf_index += obuf_index; + + } + + if (FD_ISSET(fd, &ofds)) { + ssize_t r; + assert(ibuf_length); + + if ((r = write(fd, ibuf + ibuf_index, ibuf_length)) < 0) { + pa_log(__FILE__": write() failed: %s\n", strerror(errno)); + goto fail; + } + + ibuf_length -= (size_t) r; + ibuf_index += obuf_index; + + } + + FD_ZERO(&ifds); + FD_ZERO(&ofds); + + if (obuf_length <= 0) + FD_SET(fd, &ifds); + else + FD_SET(1, &ofds); + + if (ibuf_length <= 0) + FD_SET(0, &ifds); + else + FD_SET(fd, &ofds); + } + + + ret = 0; + +fail: + if (fd >= 0) + close(fd); + + return ret; +} diff --git a/src/utils/pactl.c b/src/utils/pactl.c new file mode 100644 index 00000000..23bd924b --- /dev/null +++ b/src/utils/pactl.c @@ -0,0 +1,784 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#if PA_API_VERSION != 8 +#error Invalid Polypaudio API version +#endif + +#define BUFSIZE 1024 + +static pa_context *context = NULL; +static pa_mainloop_api *mainloop_api = NULL; + +static char *device = NULL, *sample_name = NULL; + +static SNDFILE *sndfile = NULL; +static pa_stream *sample_stream = NULL; +static pa_sample_spec sample_spec; +static size_t sample_length = 0; + +static int actions = 1; + +static int nl = 0; + +static enum { + NONE, + EXIT, + STAT, + UPLOAD_SAMPLE, + PLAY_SAMPLE, + REMOVE_SAMPLE, + LIST +} action = NONE; + +static void quit(int ret) { + assert(mainloop_api); + mainloop_api->quit(mainloop_api, ret); +} + + +static void context_drain_complete(pa_context *c, void *userdata) { + pa_context_disconnect(c); +} + +static void drain(void) { + pa_operation *o; + if (!(o = pa_context_drain(context, context_drain_complete, NULL))) + pa_context_disconnect(context); + else + pa_operation_unref(o); +} + + +static void complete_action(void) { + assert(actions > 0); + + if (!(--actions)) + drain(); +} + +static void stat_callback(pa_context *c, const pa_stat_info *i, void *userdata) { + char s[128]; + if (!i) { + fprintf(stderr, "Failed to get statistics: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + return; + } + + pa_bytes_snprint(s, sizeof(s), i->memblock_total_size); + printf("Currently in use: %u blocks containing %s bytes total.\n", i->memblock_total, s); + + pa_bytes_snprint(s, sizeof(s), i->memblock_allocated_size); + printf("Allocated during whole lifetime: %u blocks containing %s bytes total.\n", i->memblock_allocated, s); + + pa_bytes_snprint(s, sizeof(s), i->scache_size); + printf("Sample cache size: %s\n", s); + + complete_action(); +} + +static void get_server_info_callback(pa_context *c, const pa_server_info *i, void *useerdata) { + char s[PA_SAMPLE_SPEC_SNPRINT_MAX]; + + if (!i) { + fprintf(stderr, "Failed to get server information: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + return; + } + + pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec); + + printf("User name: %s\n" + "Host Name: %s\n" + "Server Name: %s\n" + "Server Version: %s\n" + "Default Sample Specification: %s\n" + "Default Sink: %s\n" + "Default Source: %s\n" + "Cookie: %08x\n", + i->user_name, + i->host_name, + i->server_name, + i->server_version, + s, + i->default_sink_name, + i->default_source_name, + i->cookie); + + complete_action(); +} + +static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) { + char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + + if (is_last < 0) { + fprintf(stderr, "Failed to get sink information: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + return; + } + + if (is_last) { + complete_action(); + return; + } + + assert(i); + + if (nl) + printf("\n"); + nl = 1; + + printf("*** Sink #%u ***\n" + "Name: %s\n" + "Driver: %s\n" + "Description: %s\n" + "Sample Specification: %s\n" + "Channel Map: %s\n" + "Owner Module: %u\n" + "Volume: %s\n" + "Monitor Source: %u\n" + "Latency: %0.0f usec\n", + i->index, + i->name, + i->driver, + i->description, + pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec), + pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), + i->owner_module, + pa_cvolume_snprint(cv, sizeof(cv), &i->volume), + i->monitor_source, + (double) i->latency); + +} + +static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) { + char s[PA_SAMPLE_SPEC_SNPRINT_MAX], t[32], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + + if (is_last < 0) { + fprintf(stderr, "Failed to get source information: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + return; + } + + if (is_last) { + complete_action(); + return; + } + + assert(i); + + if (nl) + printf("\n"); + nl = 1; + + snprintf(t, sizeof(t), "%u", i->monitor_of_sink); + + printf("*** Source #%u ***\n" + "Name: %s\n" + "Driver: %s\n" + "Description: %s\n" + "Sample Specification: %s\n" + "Channel Map: %s\n" + "Owner Module: %u\n" + "Monitor of Sink: %s\n" + "Latency: %0.0f usec\n", + i->index, + i->driver, + i->name, + i->description, + pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec), + pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), + i->owner_module, + i->monitor_of_sink != PA_INVALID_INDEX ? t : "no", + (double) i->latency); + +} + +static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) { + char t[32]; + + if (is_last < 0) { + fprintf(stderr, "Failed to get module information: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + return; + } + + if (is_last) { + complete_action(); + return; + } + + assert(i); + + if (nl) + printf("\n"); + nl = 1; + + snprintf(t, sizeof(t), "%u", i->n_used); + + printf("*** Module #%u ***\n" + "Name: %s\n" + "Argument: %s\n" + "Usage counter: %s\n" + "Auto unload: %s\n", + i->index, + i->name, + i->argument, + i->n_used != PA_INVALID_INDEX ? t : "n/a", + i->auto_unload ? "yes" : "no"); +} + +static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) { + char t[32]; + + if (is_last < 0) { + fprintf(stderr, "Failed to get client information: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + return; + } + + if (is_last) { + complete_action(); + return; + } + + assert(i); + + if (nl) + printf("\n"); + nl = 1; + + snprintf(t, sizeof(t), "%u", i->owner_module); + + printf("*** Client #%u ***\n" + "Name: %s\n" + "Driver: %s\n" + "Owner Module: %s\n", + i->index, + i->name, + i->driver, + i->owner_module != PA_INVALID_INDEX ? t : "n/a"); +} + +static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) { + char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + + if (is_last < 0) { + fprintf(stderr, "Failed to get sink input information: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + return; + } + + if (is_last) { + complete_action(); + return; + } + + assert(i); + + if (nl) + printf("\n"); + nl = 1; + + snprintf(t, sizeof(t), "%u", i->owner_module); + snprintf(k, sizeof(k), "%u", i->client); + + printf("*** Sink Input #%u ***\n" + "Name: %s\n" + "Driver: %s\n" + "Owner Module: %s\n" + "Client: %s\n" + "Sink: %u\n" + "Sample Specification: %s\n" + "Channel Map: %s\n" + "Volume: %s\n" + "Buffer Latency: %0.0f usec\n" + "Sink Latency: %0.0f usec\n" + "Resample method: %s\n", + i->index, + i->name, + i->driver, + i->owner_module != PA_INVALID_INDEX ? t : "n/a", + i->client != PA_INVALID_INDEX ? k : "n/a", + i->sink, + pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec), + pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), + pa_cvolume_snprint(cv, sizeof(cv), &i->volume), + (double) i->buffer_usec, + (double) i->sink_usec, + i->resample_method ? i->resample_method : "n/a"); +} + + +static void get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int is_last, void *userdata) { + char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + + if (is_last < 0) { + fprintf(stderr, "Failed to get source output information: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + return; + } + + if (is_last) { + complete_action(); + return; + } + + assert(i); + + if (nl) + printf("\n"); + nl = 1; + + + snprintf(t, sizeof(t), "%u", i->owner_module); + snprintf(k, sizeof(k), "%u", i->client); + + printf("*** Source Output #%u ***\n" + "Name: %s\n" + "Driver: %s\n" + "Owner Module: %s\n" + "Client: %s\n" + "Source: %u\n" + "Sample Specification: %s\n" + "Channel Map: %s\n" + "Buffer Latency: %0.0f usec\n" + "Source Latency: %0.0f usec\n" + "Resample method: %s\n", + i->index, + i->name, + i->driver, + i->owner_module != PA_INVALID_INDEX ? t : "n/a", + i->client != PA_INVALID_INDEX ? k : "n/a", + i->source, + pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec), + pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), + (double) i->buffer_usec, + (double) i->source_usec, + i->resample_method ? i->resample_method : "n/a"); +} + +static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) { + char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + + if (is_last < 0) { + fprintf(stderr, "Failed to get sample information: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + return; + } + + if (is_last) { + complete_action(); + return; + } + + assert(i); + + if (nl) + printf("\n"); + nl = 1; + + + pa_bytes_snprint(t, sizeof(t), i->bytes); + + printf("*** Sample #%u ***\n" + "Name: %s\n" + "Volume: %s\n" + "Sample Specification: %s\n" + "Channel Map: %s\n" + "Duration: %0.1fs\n" + "Size: %s\n" + "Lazy: %s\n" + "Filename: %s\n", + i->index, + i->name, + pa_cvolume_snprint(cv, sizeof(cv), &i->volume), + pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : "n/a", + pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : "n/a", + (double) i->duration/1000000, + t, + i->lazy ? "yes" : "no", + i->filename ? i->filename : "n/a"); +} + +static void get_autoload_info_callback(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata) { + if (is_last < 0) { + fprintf(stderr, "Failed to get autoload information: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + return; + } + + if (is_last) { + complete_action(); + return; + } + + assert(i); + + if (nl) + printf("\n"); + nl = 1; + + printf("*** Autoload Entry #%u ***\n" + "Name: %s\n" + "Type: %s\n" + "Module: %s\n" + "Argument: %s\n", + i->index, + i->name, + i->type == PA_AUTOLOAD_SINK ? "sink" : "source", + i->module, + i->argument); +} + +static void simple_callback(pa_context *c, int success, void *userdata) { + if (!success) { + fprintf(stderr, "Failure: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + return; + } + + complete_action(); +} + +static void stream_state_callback(pa_stream *s, void *userdata) { + assert(s); + + switch (pa_stream_get_state(s)) { + case PA_STREAM_CREATING: + case PA_STREAM_READY: + break; + + case PA_STREAM_TERMINATED: + drain(); + break; + + case PA_STREAM_FAILED: + default: + fprintf(stderr, "Failed to upload sample: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s)))); + quit(1); + } +} + +static void stream_write_callback(pa_stream *s, size_t length, void *userdata) { + sf_count_t l; + float *d; + assert(s && length && sndfile); + + d = malloc(length); + assert(d); + + assert(sample_length >= length); + l = length/pa_frame_size(&sample_spec); + + if ((sf_readf_float(sndfile, d, l)) != l) { + free(d); + fprintf(stderr, "Premature end of file\n"); + quit(1); + } + + pa_stream_write(s, d, length, free, 0); + + sample_length -= length; + + if (sample_length <= 0) { + pa_stream_set_write_callback(sample_stream, NULL, NULL); + pa_stream_finish_upload(sample_stream); + } +} + +static void context_state_callback(pa_context *c, void *userdata) { + assert(c); + switch (pa_context_get_state(c)) { + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + + case PA_CONTEXT_READY: + switch (action) { + case STAT: + actions = 2; + pa_operation_unref(pa_context_stat(c, stat_callback, NULL)); + pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL)); + break; + + case PLAY_SAMPLE: + pa_operation_unref(pa_context_play_sample(c, sample_name, device, PA_VOLUME_NORM, simple_callback, NULL)); + break; + + case REMOVE_SAMPLE: + pa_operation_unref(pa_context_remove_sample(c, sample_name, simple_callback, NULL)); + break; + + case UPLOAD_SAMPLE: + sample_stream = pa_stream_new(c, sample_name, &sample_spec, NULL); + assert(sample_stream); + + pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL); + pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL); + pa_stream_connect_upload(sample_stream, sample_length); + break; + + case EXIT: + pa_context_exit_daemon(c); + drain(); + + case LIST: + actions = 8; + pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL)); + pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL)); + pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL)); + pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL)); + pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL)); + pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL)); + pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL)); + pa_operation_unref(pa_context_get_autoload_info_list(c, get_autoload_info_callback, NULL)); + break; + + default: + assert(0); + } + break; + + case PA_CONTEXT_TERMINATED: + quit(0); + break; + + case PA_CONTEXT_FAILED: + default: + fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + } +} + +static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) { + fprintf(stderr, "Got SIGINT, exiting.\n"); + quit(0); +} + +static void help(const char *argv0) { + + printf("%s [options] stat\n" + "%s [options] list\n" + "%s [options] exit\n" + "%s [options] upload-sample FILENAME [NAME]\n" + "%s [options] play-sample NAME [SINK]\n" + "%s [options] remove-sample NAME\n\n" + " -h, --help Show this help\n" + " --version Show version\n\n" + " -s, --server=SERVER The name of the server to connect to\n" + " -n, --client-name=NAME How to call this client on the server\n", + argv0, argv0, argv0, argv0, argv0, argv0); +} + +enum { ARG_VERSION = 256 }; + +int main(int argc, char *argv[]) { + pa_mainloop* m = NULL; + char tmp[PATH_MAX]; + int ret = 1, r, c; + char *server = NULL, *client_name = NULL, *bn; + + static const struct option long_options[] = { + {"server", 1, NULL, 's'}, + {"client-name", 1, NULL, 'n'}, + {"version", 0, NULL, ARG_VERSION}, + {"help", 0, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + if (!(bn = strrchr(argv[0], '/'))) + bn = argv[0]; + else + bn++; + + while ((c = getopt_long(argc, argv, "s:n:h", long_options, NULL)) != -1) { + switch (c) { + case 'h' : + help(bn); + ret = 0; + goto quit; + + case ARG_VERSION: + printf("pactl "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version()); + ret = 0; + goto quit; + + case 's': + free(server); + server = strdup(optarg); + break; + + case 'n': + free(client_name); + client_name = strdup(optarg); + break; + + default: + goto quit; + } + } + + if (!client_name) + client_name = strdup(bn); + + if (optind < argc) { + if (!strcmp(argv[optind], "stat")) + action = STAT; + else if (!strcmp(argv[optind], "exit")) + action = EXIT; + else if (!strcmp(argv[optind], "list")) + action = LIST; + else if (!strcmp(argv[optind], "upload-sample")) { + struct SF_INFO sfinfo; + action = UPLOAD_SAMPLE; + + if (optind+1 >= argc) { + fprintf(stderr, "Please specify a sample file to load\n"); + goto quit; + } + + if (optind+2 < argc) + sample_name = strdup(argv[optind+2]); + else { + char *f = strrchr(argv[optind+1], '/'); + size_t n; + if (f) + f++; + else + f = argv[optind]; + + n = strcspn(f, "."); + strncpy(tmp, f, n); + tmp[n] = 0; + sample_name = strdup(tmp); + } + + memset(&sfinfo, 0, sizeof(sfinfo)); + if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfinfo))) { + fprintf(stderr, "Failed to open sound file.\n"); + goto quit; + } + + sample_spec.format = PA_SAMPLE_FLOAT32; + sample_spec.rate = sfinfo.samplerate; + sample_spec.channels = sfinfo.channels; + + sample_length = sfinfo.frames*pa_frame_size(&sample_spec); + } else if (!strcmp(argv[optind], "play-sample")) { + action = PLAY_SAMPLE; + if (optind+1 >= argc) { + fprintf(stderr, "You have to specify a sample name to play\n"); + goto quit; + } + + sample_name = strdup(argv[optind+1]); + + if (optind+2 < argc) + device = strdup(argv[optind+2]); + + } else if (!strcmp(argv[optind], "remove-sample")) { + action = REMOVE_SAMPLE; + if (optind+1 >= argc) { + fprintf(stderr, "You have to specify a sample name to remove\n"); + goto quit; + } + + sample_name = strdup(argv[optind+1]); + } + } + + if (action == NONE) { + fprintf(stderr, "No valid command specified.\n"); + goto quit; + } + + if (!(m = pa_mainloop_new())) { + fprintf(stderr, "pa_mainloop_new() failed.\n"); + goto quit; + } + + mainloop_api = pa_mainloop_get_api(m); + + r = pa_signal_init(mainloop_api); + assert(r == 0); + pa_signal_new(SIGINT, exit_signal_callback, NULL); +#ifdef SIGPIPE + signal(SIGPIPE, SIG_IGN); +#endif + + if (!(context = pa_context_new(mainloop_api, client_name))) { + fprintf(stderr, "pa_context_new() failed.\n"); + goto quit; + } + + pa_context_set_state_callback(context, context_state_callback, NULL); + pa_context_connect(context, server, 1, NULL); + + if (pa_mainloop_run(m, &ret) < 0) { + fprintf(stderr, "pa_mainloop_run() failed.\n"); + goto quit; + } + +quit: + if (sample_stream) + pa_stream_unref(sample_stream); + + if (context) + pa_context_unref(context); + + if (m) { + pa_signal_done(); + pa_mainloop_free(m); + } + + if (sndfile) + sf_close(sndfile); + + free(server); + free(device); + free(sample_name); + + return ret; +} diff --git a/src/utils/paplay.c b/src/utils/paplay.c new file mode 100644 index 00000000..ddc1cbc1 --- /dev/null +++ b/src/utils/paplay.c @@ -0,0 +1,386 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#if PA_API_VERSION != 8 +#error Invalid Polypaudio API version +#endif + +static pa_context *context = NULL; +static pa_stream *stream = NULL; +static pa_mainloop_api *mainloop_api = NULL; + +static char *stream_name = NULL, *client_name = NULL, *device = NULL; + +static int verbose = 0; +static pa_volume_t volume = PA_VOLUME_NORM; + +static SNDFILE* sndfile = NULL; + +static pa_sample_spec sample_spec = { 0, 0, 0 }; + +static sf_count_t (*readf_function)(SNDFILE *_sndfile, void *ptr, sf_count_t frames); + +/* A shortcut for terminating the application */ +static void quit(int ret) { + assert(mainloop_api); + mainloop_api->quit(mainloop_api, ret); +} + +/* Connection draining complete */ +static void context_drain_complete(pa_context*c, void *userdata) { + pa_context_disconnect(c); +} + +/* Stream draining complete */ +static void stream_drain_complete(pa_stream*s, int success, void *userdata) { + pa_operation *o; + + if (!success) { + fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context))); + quit(1); + } + + if (verbose) + fprintf(stderr, "Playback stream drained.\n"); + + pa_stream_disconnect(stream); + pa_stream_unref(stream); + stream = NULL; + + if (!(o = pa_context_drain(context, context_drain_complete, NULL))) + pa_context_disconnect(context); + else { + pa_operation_unref(o); + + if (verbose) + fprintf(stderr, "Draining connection to server.\n"); + } +} + +/* This is called whenever new data may be written to the stream */ +static void stream_write_callback(pa_stream *s, size_t length, void *userdata) { + size_t k; + sf_count_t f, n; + void *data; + assert(s && length); + + if (!sndfile) + return; + + k = pa_frame_size(&sample_spec); + + data = malloc(length); + + n = length/k; + + f = readf_function(sndfile, data, n); + + if (f > 0) + pa_stream_write(s, data, f*k, free, 0); + + if (f < n) { + sf_close(sndfile); + sndfile = NULL; + pa_operation_unref(pa_stream_drain(s, stream_drain_complete, NULL)); + } +} + +/* This routine is called whenever the stream state changes */ +static void stream_state_callback(pa_stream *s, void *userdata) { + assert(s); + + switch (pa_stream_get_state(s)) { + case PA_STREAM_CREATING: + case PA_STREAM_TERMINATED: + break; + + case PA_STREAM_READY: + if (verbose) + fprintf(stderr, "Stream successfully created\n"); + break; + + case PA_STREAM_FAILED: + default: + fprintf(stderr, "Stream errror: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s)))); + quit(1); + } +} + +/* This is called whenever the context status changes */ +static void context_state_callback(pa_context *c, void *userdata) { + assert(c); + + switch (pa_context_get_state(c)) { + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + + case PA_CONTEXT_READY: { + pa_cvolume cv; + + assert(c && !stream); + + if (verbose) + fprintf(stderr, "Connection established.\n"); + + stream = pa_stream_new(c, stream_name, &sample_spec, NULL); + assert(stream); + + pa_stream_set_state_callback(stream, stream_state_callback, NULL); + pa_stream_set_write_callback(stream, stream_write_callback, NULL); + pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, PA_CHANNELS_MAX, volume)); + + break; + } + + case PA_CONTEXT_TERMINATED: + quit(0); + break; + + case PA_CONTEXT_FAILED: + default: + fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + } +} + +/* UNIX signal to quit recieved */ +static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) { + if (verbose) + fprintf(stderr, "Got SIGINT, exiting.\n"); + quit(0); + +} + +static void help(const char *argv0) { + + printf("%s [options] [FILE]\n\n" + " -h, --help Show this help\n" + " --version Show version\n\n" + " -v, --verbose Enable verbose operations\n\n" + " -s, --server=SERVER The name of the server to connect to\n" + " -d, --device=DEVICE The name of the sink/source to connect to\n" + " -n, --client-name=NAME How to call this client on the server\n" + " --stream-name=NAME How to call this stream on the server\n" + " --volume=VOLUME Specify the initial (linear) volume in range 0...256\n", + argv0); +} + +enum { + ARG_VERSION = 256, + ARG_STREAM_NAME, + ARG_VOLUME +}; + +int main(int argc, char *argv[]) { + pa_mainloop* m = NULL; + int ret = 1, r, c; + char *bn, *server = NULL; + const char *filename; + SF_INFO sfinfo; + + static const struct option long_options[] = { + {"device", 1, NULL, 'd'}, + {"server", 1, NULL, 's'}, + {"client-name", 1, NULL, 'n'}, + {"stream-name", 1, NULL, ARG_STREAM_NAME}, + {"version", 0, NULL, ARG_VERSION}, + {"help", 0, NULL, 'h'}, + {"verbose", 0, NULL, 'v'}, + {"volume", 1, NULL, ARG_VOLUME}, + {NULL, 0, NULL, 0} + }; + + if (!(bn = strrchr(argv[0], '/'))) + bn = argv[0]; + else + bn++; + + while ((c = getopt_long(argc, argv, "d:s:n:hv", long_options, NULL)) != -1) { + + switch (c) { + case 'h' : + help(bn); + ret = 0; + goto quit; + + case ARG_VERSION: + printf("paplay "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version()); + ret = 0; + goto quit; + + case 'd': + free(device); + device = strdup(optarg); + break; + + case 's': + free(server); + server = strdup(optarg); + break; + + case 'n': + free(client_name); + client_name = strdup(optarg); + break; + + case ARG_STREAM_NAME: + free(stream_name); + stream_name = strdup(optarg); + break; + + case 'v': + verbose = 1; + break; + + case ARG_VOLUME: { + int v = atoi(optarg); + volume = v < 0 ? 0 : v; + break; + } + + default: + goto quit; + } + } + + + filename = optind < argc ? argv[optind] : "STDIN"; + + + if (!client_name) + client_name = strdup(bn); + + if (!stream_name) + stream_name = strdup(filename); + + memset(&sfinfo, 0, sizeof(sfinfo)); + + if (optind < argc) + sndfile = sf_open(filename, SFM_READ, &sfinfo); + else + sndfile = sf_open_fd(STDIN_FILENO, SFM_READ, &sfinfo, 0); + + if (!sndfile) { + fprintf(stderr, "Failed to open file '%s'\n", filename); + goto quit; + } + + sample_spec.rate = sfinfo.samplerate; + sample_spec.channels = sfinfo.channels; + + switch (sfinfo.format & 0xFF) { + case SF_FORMAT_PCM_16: + case SF_FORMAT_PCM_U8: + case SF_FORMAT_ULAW: + case SF_FORMAT_ALAW: + sample_spec.format = PA_SAMPLE_S16NE; + readf_function = (sf_count_t (*)(SNDFILE *_sndfile, void *ptr, sf_count_t frames)) sf_readf_short; + break; + case SF_FORMAT_FLOAT: + default: + sample_spec.format = PA_SAMPLE_FLOAT32NE; + readf_function = (sf_count_t (*)(SNDFILE *_sndfile, void *ptr, sf_count_t frames)) sf_readf_float; + break; + } + + if (verbose) { + char t[PA_SAMPLE_SPEC_SNPRINT_MAX]; + pa_sample_spec_snprint(t, sizeof(t), &sample_spec); + fprintf(stderr, "Using sample spec '%s'\n", t); + } + + /* Set up a new main loop */ + if (!(m = pa_mainloop_new())) { + fprintf(stderr, "pa_mainloop_new() failed.\n"); + goto quit; + } + + mainloop_api = pa_mainloop_get_api(m); + + r = pa_signal_init(mainloop_api); + assert(r == 0); + pa_signal_new(SIGINT, exit_signal_callback, NULL); +#ifdef SIGPIPE + signal(SIGPIPE, SIG_IGN); +#endif + + /* Create a new connection context */ + if (!(context = pa_context_new(mainloop_api, client_name))) { + fprintf(stderr, "pa_context_new() failed.\n"); + goto quit; + } + + pa_context_set_state_callback(context, context_state_callback, NULL); + + /* Connect the context */ + pa_context_connect(context, server, 1, NULL); + + /* Run the main loop */ + if (pa_mainloop_run(m, &ret) < 0) { + fprintf(stderr, "pa_mainloop_run() failed.\n"); + goto quit; + } + +quit: + if (stream) + pa_stream_unref(stream); + + if (context) + pa_context_unref(context); + + if (m) { + pa_signal_done(); + pa_mainloop_free(m); + } + + free(server); + free(device); + free(client_name); + free(stream_name); + + if (sndfile) + sf_close(sndfile); + + return ret; +} diff --git a/src/utils/pax11publish.c b/src/utils/pax11publish.c new file mode 100644 index 00000000..63ee4821 --- /dev/null +++ b/src/utils/pax11publish.c @@ -0,0 +1,217 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + const char *dname = NULL, *sink = NULL, *source = NULL, *server = NULL, *cookie_file = PA_NATIVE_COOKIE_FILE; + int c, ret = 1; + Display *d = NULL; + enum { DUMP, EXPORT, IMPORT, REMOVE } mode = DUMP; + + while ((c = getopt(argc, argv, "deiD:S:O:I:c:hr")) != -1) { + switch (c) { + case 'D' : + dname = optarg; + break; + case 'h': + printf("%s [-D display] [-S server] [-O sink] [-I source] [-c file] [-d|-e|-i|-r]\n\n" + " -d Show current Polypaudio data attached to X11 display (default)\n" + " -e Export local Polypaudio data to X11 display\n" + " -i Import Polypaudio data from X11 display to local environment variables and cookie file.\n" + " -r Remove Polypaudio data from X11 display\n", + pa_path_get_filename(argv[0])); + ret = 0; + goto finish; + case 'd': + mode = DUMP; + break; + case 'e': + mode = EXPORT; + break; + case 'i': + mode = IMPORT; + break; + case 'r': + mode = REMOVE; + break; + case 'c': + cookie_file = optarg; + break; + case 'I': + source = optarg; + break; + case 'O': + sink = optarg; + break; + case 'S': + server = optarg; + break; + default: + fprintf(stderr, "Failed to parse command line.\n"); + goto finish; + } + } + + if (!(d = XOpenDisplay(dname))) { + pa_log(__FILE__": XOpenDisplay() failed\n"); + goto finish; + } + + switch (mode) { + case DUMP: { + char t[1024]; + if (pa_x11_get_prop(d, "POLYP_SERVER", t, sizeof(t))) + printf("Server: %s\n", t); + if (pa_x11_get_prop(d, "POLYP_SOURCE", t, sizeof(t))) + printf("Source: %s\n", t); + if (pa_x11_get_prop(d, "POLYP_SINK", t, sizeof(t))) + printf("Sink: %s\n", t); + if (pa_x11_get_prop(d, "POLYP_COOKIE", t, sizeof(t))) + printf("Cookie: %s\n", t); + + break; + } + + case IMPORT: { + char t[1024]; + if (pa_x11_get_prop(d, "POLYP_SERVER", t, sizeof(t))) + printf("POLYP_SERVER='%s'\nexport POLYP_SERVER\n", t); + if (pa_x11_get_prop(d, "POLYP_SOURCE", t, sizeof(t))) + printf("POLYP_SOURCE='%s'\nexport POLYP_SOURCE\n", t); + if (pa_x11_get_prop(d, "POLYP_SINK", t, sizeof(t))) + printf("POLYP_SINK='%s'\nexport POLYP_SINK\n", t); + + if (pa_x11_get_prop(d, "POLYP_COOKIE", t, sizeof(t))) { + uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; + size_t l; + if ((l = pa_parsehex(t, cookie, sizeof(cookie))) != sizeof(cookie)) { + fprintf(stderr, "Failed to parse cookie data\n"); + goto finish; + } + + if (pa_authkey_save(cookie_file, cookie, l) < 0) { + fprintf(stderr, "Failed to save cookie data\n"); + goto finish; + } + } + + break; + } + + case EXPORT: { + pa_client_conf *conf = pa_client_conf_new(); + uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; + char hx[PA_NATIVE_COOKIE_LENGTH*2+1]; + assert(conf); + + if (pa_client_conf_load(conf, NULL) < 0) { + fprintf(stderr, "Failed to load client configuration file.\n"); + goto finish; + } + + if (pa_client_conf_env(conf) < 0) { + fprintf(stderr, "Failed to read environment configuration data.\n"); + goto finish; + } + + pa_x11_del_prop(d, "POLYP_SERVER"); + pa_x11_del_prop(d, "POLYP_SINK"); + pa_x11_del_prop(d, "POLYP_SOURCE"); + pa_x11_del_prop(d, "POLYP_ID"); + pa_x11_del_prop(d, "POLYP_COOKIE"); + + if (server) + pa_x11_set_prop(d, "POLYP_SERVER", server); + else if (conf->default_server) + pa_x11_set_prop(d, "POLYP_SERVER", conf->default_server); + else { + char hn[256]; + if (!pa_get_fqdn(hn, sizeof(hn))) { + fprintf(stderr, "Failed to get FQDN.\n"); + goto finish; + } + + pa_x11_set_prop(d, "POLYP_SERVER", hn); + } + + if (sink) + pa_x11_set_prop(d, "POLYP_SINK", sink); + else if (conf->default_sink) + pa_x11_set_prop(d, "POLYP_SINK", conf->default_sink); + + if (source) + pa_x11_set_prop(d, "POLYP_SOURCE", source); + if (conf->default_source) + pa_x11_set_prop(d, "POLYP_SOURCE", conf->default_source); + + pa_client_conf_free(conf); + + if (pa_authkey_load_auto(cookie_file, cookie, sizeof(cookie)) < 0) { + fprintf(stderr, "Failed to load cookie data\n"); + goto finish; + } + + pa_x11_set_prop(d, "POLYP_COOKIE", pa_hexstr(cookie, sizeof(cookie), hx, sizeof(hx))); + break; + } + + case REMOVE: + pa_x11_del_prop(d, "POLYP_SERVER"); + pa_x11_del_prop(d, "POLYP_SINK"); + pa_x11_del_prop(d, "POLYP_SOURCE"); + pa_x11_del_prop(d, "POLYP_ID"); + pa_x11_del_prop(d, "POLYP_COOKIE"); + break; + + default: + fprintf(stderr, "No yet implemented.\n"); + goto finish; + } + + ret = 0; + +finish: + + if (d) { + XSync(d, False); + XCloseDisplay(d); + } + + return ret; +} -- cgit