From 41f6aea8fdbc744c13bc461056a2d694a5c4d06f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 17 Jul 2004 14:12:30 +0000 Subject: rename src to polyp git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@90 fefdeb5f-60dc-0310-8127-8f9354f1896f --- polyp/Makefile.am | 363 ++++++++++++++++ polyp/alsa-util.c | 98 +++++ polyp/alsa-util.h | 35 ++ polyp/authkey.c | 151 +++++++ polyp/authkey.h | 31 ++ polyp/cli-command.c | 557 +++++++++++++++++++++++++ polyp/cli-command.h | 32 ++ polyp/cli.c | 148 +++++++ polyp/cli.h | 36 ++ polyp/client.c | 79 ++++ polyp/client.h | 51 +++ polyp/clitext.c | 203 +++++++++ polyp/clitext.h | 35 ++ polyp/cmdline.c | 111 +++++ polyp/cmdline.h | 36 ++ polyp/core.c | 88 ++++ polyp/core.h | 45 ++ polyp/depmod.py | 73 ++++ polyp/dynarray.c | 98 +++++ polyp/dynarray.h | 37 ++ polyp/endianmacros.h | 62 +++ polyp/esound.h | 213 ++++++++++ polyp/hashmap.c | 170 ++++++++ polyp/hashmap.h | 37 ++ polyp/idxset.c | 397 ++++++++++++++++++ polyp/idxset.h | 63 +++ polyp/iochannel.c | 222 ++++++++++ polyp/iochannel.h | 50 +++ polyp/ioline.c | 220 ++++++++++ polyp/ioline.h | 35 ++ polyp/main.c | 182 ++++++++ polyp/mainloop-api.c | 60 +++ polyp/mainloop-api.h | 65 +++ polyp/mainloop-signal.c | 163 ++++++++ polyp/mainloop-signal.h | 33 ++ polyp/mainloop.c | 553 ++++++++++++++++++++++++ polyp/mainloop.h | 37 ++ polyp/memblock.c | 113 +++++ polyp/memblock.h | 49 +++ polyp/memblockq.c | 326 +++++++++++++++ polyp/memblockq.h | 82 ++++ polyp/memchunk.c | 149 +++++++ polyp/memchunk.h | 41 ++ polyp/modargs.c | 288 +++++++++++++ polyp/modargs.h | 42 ++ polyp/module-alsa-sink.c | 259 ++++++++++++ polyp/module-alsa-source.c | 237 +++++++++++ polyp/module-cli.c | 73 ++++ polyp/module-oss-mmap.c | 404 ++++++++++++++++++ polyp/module-oss.c | 304 ++++++++++++++ polyp/module-pipe-sink.c | 218 ++++++++++ polyp/module-protocol-stub.c | 165 ++++++++ polyp/module.c | 161 +++++++ polyp/module.h | 55 +++ polyp/namereg.c | 136 ++++++ polyp/namereg.h | 38 ++ polyp/native-common.h | 68 +++ polyp/oss-util.c | 163 ++++++++ polyp/oss-util.h | 32 ++ polyp/pacat-simple.c | 83 ++++ polyp/pacat.c | 336 +++++++++++++++ polyp/packet.c | 72 ++++ polyp/packet.h | 41 ++ polyp/pactl.c | 166 ++++++++ polyp/parec-simple.c | 94 +++++ polyp/pdispatch.c | 247 +++++++++++ polyp/pdispatch.h | 52 +++ polyp/polypaudio.pa | 41 ++ polyp/polyplib-def.h | 40 ++ polyp/polyplib-error.c | 53 +++ polyp/polyplib-error.h | 29 ++ polyp/polyplib-simple.c | 259 ++++++++++++ polyp/polyplib-simple.h | 49 +++ polyp/polyplib.c | 973 +++++++++++++++++++++++++++++++++++++++++++ polyp/polyplib.h | 94 +++++ polyp/protocol-cli.c | 85 ++++ polyp/protocol-cli.h | 35 ++ polyp/protocol-esound.c | 847 +++++++++++++++++++++++++++++++++++++ polyp/protocol-esound.h | 35 ++ polyp/protocol-native.c | 863 ++++++++++++++++++++++++++++++++++++++ polyp/protocol-native.h | 35 ++ polyp/protocol-simple.c | 444 ++++++++++++++++++++ polyp/protocol-simple.h | 35 ++ polyp/pstream-util.c | 60 +++ polyp/pstream-util.h | 35 ++ polyp/pstream.c | 457 ++++++++++++++++++++ polyp/pstream.h | 51 +++ polyp/queue.c | 109 +++++ polyp/queue.h | 34 ++ polyp/resampler.c | 180 ++++++++ polyp/resampler.h | 37 ++ polyp/sample-util.c | 144 +++++++ polyp/sample-util.h | 48 +++ polyp/sample.c | 99 +++++ polyp/sample.h | 64 +++ polyp/sconv-s16be.c | 34 ++ polyp/sconv-s16be.h | 28 ++ polyp/sconv-s16le.c | 82 ++++ polyp/sconv-s16le.h | 28 ++ polyp/sconv.c | 137 ++++++ polyp/sconv.h | 33 ++ polyp/sink-input.c | 163 ++++++++ polyp/sink-input.h | 67 +++ polyp/sink.c | 292 +++++++++++++ polyp/sink.h | 67 +++ polyp/sioman.c | 42 ++ polyp/sioman.h | 28 ++ polyp/socket-client.c | 236 +++++++++++ polyp/socket-client.h | 41 ++ polyp/socket-server.c | 209 ++++++++++ polyp/socket-server.h | 41 ++ polyp/socket-util.c | 216 ++++++++++ polyp/socket-util.h | 41 ++ polyp/source-output.c | 101 +++++ polyp/source-output.h | 58 +++ polyp/source.c | 131 ++++++ polyp/source.h | 61 +++ polyp/strbuf.c | 164 ++++++++ polyp/strbuf.h | 36 ++ polyp/tagstruct.c | 241 +++++++++++ polyp/tagstruct.h | 53 +++ polyp/tokenizer.c | 89 ++++ polyp/tokenizer.h | 32 ++ polyp/util.c | 147 +++++++ polyp/util.h | 38 ++ src/Makefile.am | 363 ---------------- src/alsa-util.c | 98 ----- src/alsa-util.h | 35 -- src/authkey.c | 151 ------- src/authkey.h | 31 -- src/cli-command.c | 557 ------------------------- src/cli-command.h | 32 -- src/cli.c | 148 ------- src/cli.h | 36 -- src/client.c | 79 ---- src/client.h | 51 --- src/clitext.c | 203 --------- src/clitext.h | 35 -- src/cmdline.c | 111 ----- src/cmdline.h | 36 -- src/core.c | 88 ---- src/core.h | 45 -- src/depmod.py | 73 ---- src/dynarray.c | 98 ----- src/dynarray.h | 37 -- src/endianmacros.h | 62 --- src/esound.h | 213 ---------- src/hashmap.c | 170 -------- src/hashmap.h | 37 -- src/idxset.c | 397 ------------------ src/idxset.h | 63 --- src/iochannel.c | 222 ---------- src/iochannel.h | 50 --- src/ioline.c | 220 ---------- src/ioline.h | 35 -- src/main.c | 182 -------- src/mainloop-api.c | 60 --- src/mainloop-api.h | 65 --- src/mainloop-signal.c | 163 -------- src/mainloop-signal.h | 33 -- src/mainloop.c | 553 ------------------------ src/mainloop.h | 37 -- src/memblock.c | 113 ----- src/memblock.h | 49 --- src/memblockq.c | 326 --------------- src/memblockq.h | 82 ---- src/memchunk.c | 149 ------- src/memchunk.h | 41 -- src/modargs.c | 288 ------------- src/modargs.h | 42 -- src/module-alsa-sink.c | 259 ------------ src/module-alsa-source.c | 237 ----------- src/module-cli.c | 73 ---- src/module-oss-mmap.c | 404 ------------------ src/module-oss.c | 304 -------------- src/module-pipe-sink.c | 218 ---------- src/module-protocol-stub.c | 165 -------- src/module.c | 161 ------- src/module.h | 55 --- src/namereg.c | 136 ------ src/namereg.h | 38 -- src/native-common.h | 68 --- src/oss-util.c | 163 -------- src/oss-util.h | 32 -- src/pacat-simple.c | 83 ---- src/pacat.c | 336 --------------- src/packet.c | 72 ---- src/packet.h | 41 -- src/pactl.c | 166 -------- src/parec-simple.c | 94 ----- src/pdispatch.c | 247 ----------- src/pdispatch.h | 52 --- src/polypaudio.pa | 41 -- src/polyplib-def.h | 40 -- src/polyplib-error.c | 53 --- src/polyplib-error.h | 29 -- src/polyplib-simple.c | 259 ------------ src/polyplib-simple.h | 49 --- src/polyplib.c | 973 ------------------------------------------- src/polyplib.h | 94 ----- src/protocol-cli.c | 85 ---- src/protocol-cli.h | 35 -- src/protocol-esound.c | 847 ------------------------------------- src/protocol-esound.h | 35 -- src/protocol-native.c | 863 -------------------------------------- src/protocol-native.h | 35 -- src/protocol-simple.c | 444 -------------------- src/protocol-simple.h | 35 -- src/pstream-util.c | 60 --- src/pstream-util.h | 35 -- src/pstream.c | 457 -------------------- src/pstream.h | 51 --- src/queue.c | 109 ----- src/queue.h | 34 -- src/resampler.c | 180 -------- src/resampler.h | 37 -- src/sample-util.c | 144 ------- src/sample-util.h | 48 --- src/sample.c | 99 ----- src/sample.h | 64 --- src/sconv-s16be.c | 34 -- src/sconv-s16be.h | 28 -- src/sconv-s16le.c | 82 ---- src/sconv-s16le.h | 28 -- src/sconv.c | 137 ------ src/sconv.h | 33 -- src/sink-input.c | 163 -------- src/sink-input.h | 67 --- src/sink.c | 292 ------------- src/sink.h | 67 --- src/sioman.c | 42 -- src/sioman.h | 28 -- src/socket-client.c | 236 ----------- src/socket-client.h | 41 -- src/socket-server.c | 209 ---------- src/socket-server.h | 41 -- src/socket-util.c | 216 ---------- src/socket-util.h | 41 -- src/source-output.c | 101 ----- src/source-output.h | 58 --- src/source.c | 131 ------ src/source.h | 61 --- src/strbuf.c | 164 -------- src/strbuf.h | 36 -- src/tagstruct.c | 241 ----------- src/tagstruct.h | 53 --- src/tokenizer.c | 89 ---- src/tokenizer.h | 32 -- src/util.c | 147 ------- src/util.h | 38 -- 250 files changed, 17534 insertions(+), 17534 deletions(-) create mode 100644 polyp/Makefile.am create mode 100644 polyp/alsa-util.c create mode 100644 polyp/alsa-util.h create mode 100644 polyp/authkey.c create mode 100644 polyp/authkey.h create mode 100644 polyp/cli-command.c create mode 100644 polyp/cli-command.h create mode 100644 polyp/cli.c create mode 100644 polyp/cli.h create mode 100644 polyp/client.c create mode 100644 polyp/client.h create mode 100644 polyp/clitext.c create mode 100644 polyp/clitext.h create mode 100644 polyp/cmdline.c create mode 100644 polyp/cmdline.h create mode 100644 polyp/core.c create mode 100644 polyp/core.h create mode 100755 polyp/depmod.py create mode 100644 polyp/dynarray.c create mode 100644 polyp/dynarray.h create mode 100644 polyp/endianmacros.h create mode 100644 polyp/esound.h create mode 100644 polyp/hashmap.c create mode 100644 polyp/hashmap.h create mode 100644 polyp/idxset.c create mode 100644 polyp/idxset.h create mode 100644 polyp/iochannel.c create mode 100644 polyp/iochannel.h create mode 100644 polyp/ioline.c create mode 100644 polyp/ioline.h create mode 100644 polyp/main.c create mode 100644 polyp/mainloop-api.c create mode 100644 polyp/mainloop-api.h create mode 100644 polyp/mainloop-signal.c create mode 100644 polyp/mainloop-signal.h create mode 100644 polyp/mainloop.c create mode 100644 polyp/mainloop.h create mode 100644 polyp/memblock.c create mode 100644 polyp/memblock.h create mode 100644 polyp/memblockq.c create mode 100644 polyp/memblockq.h create mode 100644 polyp/memchunk.c create mode 100644 polyp/memchunk.h create mode 100644 polyp/modargs.c create mode 100644 polyp/modargs.h create mode 100644 polyp/module-alsa-sink.c create mode 100644 polyp/module-alsa-source.c create mode 100644 polyp/module-cli.c create mode 100644 polyp/module-oss-mmap.c create mode 100644 polyp/module-oss.c create mode 100644 polyp/module-pipe-sink.c create mode 100644 polyp/module-protocol-stub.c create mode 100644 polyp/module.c create mode 100644 polyp/module.h create mode 100644 polyp/namereg.c create mode 100644 polyp/namereg.h create mode 100644 polyp/native-common.h create mode 100644 polyp/oss-util.c create mode 100644 polyp/oss-util.h create mode 100644 polyp/pacat-simple.c create mode 100644 polyp/pacat.c create mode 100644 polyp/packet.c create mode 100644 polyp/packet.h create mode 100644 polyp/pactl.c create mode 100644 polyp/parec-simple.c create mode 100644 polyp/pdispatch.c create mode 100644 polyp/pdispatch.h create mode 100755 polyp/polypaudio.pa create mode 100644 polyp/polyplib-def.h create mode 100644 polyp/polyplib-error.c create mode 100644 polyp/polyplib-error.h create mode 100644 polyp/polyplib-simple.c create mode 100644 polyp/polyplib-simple.h create mode 100644 polyp/polyplib.c create mode 100644 polyp/polyplib.h create mode 100644 polyp/protocol-cli.c create mode 100644 polyp/protocol-cli.h create mode 100644 polyp/protocol-esound.c create mode 100644 polyp/protocol-esound.h create mode 100644 polyp/protocol-native.c create mode 100644 polyp/protocol-native.h create mode 100644 polyp/protocol-simple.c create mode 100644 polyp/protocol-simple.h create mode 100644 polyp/pstream-util.c create mode 100644 polyp/pstream-util.h create mode 100644 polyp/pstream.c create mode 100644 polyp/pstream.h create mode 100644 polyp/queue.c create mode 100644 polyp/queue.h create mode 100644 polyp/resampler.c create mode 100644 polyp/resampler.h create mode 100644 polyp/sample-util.c create mode 100644 polyp/sample-util.h create mode 100644 polyp/sample.c create mode 100644 polyp/sample.h create mode 100644 polyp/sconv-s16be.c create mode 100644 polyp/sconv-s16be.h create mode 100644 polyp/sconv-s16le.c create mode 100644 polyp/sconv-s16le.h create mode 100644 polyp/sconv.c create mode 100644 polyp/sconv.h create mode 100644 polyp/sink-input.c create mode 100644 polyp/sink-input.h create mode 100644 polyp/sink.c create mode 100644 polyp/sink.h create mode 100644 polyp/sioman.c create mode 100644 polyp/sioman.h create mode 100644 polyp/socket-client.c create mode 100644 polyp/socket-client.h create mode 100644 polyp/socket-server.c create mode 100644 polyp/socket-server.h create mode 100644 polyp/socket-util.c create mode 100644 polyp/socket-util.h create mode 100644 polyp/source-output.c create mode 100644 polyp/source-output.h create mode 100644 polyp/source.c create mode 100644 polyp/source.h create mode 100644 polyp/strbuf.c create mode 100644 polyp/strbuf.h create mode 100644 polyp/tagstruct.c create mode 100644 polyp/tagstruct.h create mode 100644 polyp/tokenizer.c create mode 100644 polyp/tokenizer.h create mode 100644 polyp/util.c create mode 100644 polyp/util.h delete mode 100644 src/Makefile.am delete mode 100644 src/alsa-util.c delete mode 100644 src/alsa-util.h delete mode 100644 src/authkey.c delete mode 100644 src/authkey.h delete mode 100644 src/cli-command.c delete mode 100644 src/cli-command.h delete mode 100644 src/cli.c delete mode 100644 src/cli.h delete mode 100644 src/client.c delete mode 100644 src/client.h delete mode 100644 src/clitext.c delete mode 100644 src/clitext.h delete mode 100644 src/cmdline.c delete mode 100644 src/cmdline.h delete mode 100644 src/core.c delete mode 100644 src/core.h delete mode 100755 src/depmod.py delete mode 100644 src/dynarray.c delete mode 100644 src/dynarray.h delete mode 100644 src/endianmacros.h delete mode 100644 src/esound.h delete mode 100644 src/hashmap.c delete mode 100644 src/hashmap.h delete mode 100644 src/idxset.c delete mode 100644 src/idxset.h delete mode 100644 src/iochannel.c delete mode 100644 src/iochannel.h delete mode 100644 src/ioline.c delete mode 100644 src/ioline.h delete mode 100644 src/main.c delete mode 100644 src/mainloop-api.c delete mode 100644 src/mainloop-api.h delete mode 100644 src/mainloop-signal.c delete mode 100644 src/mainloop-signal.h delete mode 100644 src/mainloop.c delete mode 100644 src/mainloop.h delete mode 100644 src/memblock.c delete mode 100644 src/memblock.h delete mode 100644 src/memblockq.c delete mode 100644 src/memblockq.h delete mode 100644 src/memchunk.c delete mode 100644 src/memchunk.h delete mode 100644 src/modargs.c delete mode 100644 src/modargs.h delete mode 100644 src/module-alsa-sink.c delete mode 100644 src/module-alsa-source.c delete mode 100644 src/module-cli.c delete mode 100644 src/module-oss-mmap.c delete mode 100644 src/module-oss.c delete mode 100644 src/module-pipe-sink.c delete mode 100644 src/module-protocol-stub.c delete mode 100644 src/module.c delete mode 100644 src/module.h delete mode 100644 src/namereg.c delete mode 100644 src/namereg.h delete mode 100644 src/native-common.h delete mode 100644 src/oss-util.c delete mode 100644 src/oss-util.h delete mode 100644 src/pacat-simple.c delete mode 100644 src/pacat.c delete mode 100644 src/packet.c delete mode 100644 src/packet.h delete mode 100644 src/pactl.c delete mode 100644 src/parec-simple.c delete mode 100644 src/pdispatch.c delete mode 100644 src/pdispatch.h delete mode 100755 src/polypaudio.pa delete mode 100644 src/polyplib-def.h delete mode 100644 src/polyplib-error.c delete mode 100644 src/polyplib-error.h delete mode 100644 src/polyplib-simple.c delete mode 100644 src/polyplib-simple.h delete mode 100644 src/polyplib.c delete mode 100644 src/polyplib.h delete mode 100644 src/protocol-cli.c delete mode 100644 src/protocol-cli.h delete mode 100644 src/protocol-esound.c delete mode 100644 src/protocol-esound.h delete mode 100644 src/protocol-native.c delete mode 100644 src/protocol-native.h delete mode 100644 src/protocol-simple.c delete mode 100644 src/protocol-simple.h delete mode 100644 src/pstream-util.c delete mode 100644 src/pstream-util.h delete mode 100644 src/pstream.c delete mode 100644 src/pstream.h delete mode 100644 src/queue.c delete mode 100644 src/queue.h delete mode 100644 src/resampler.c delete mode 100644 src/resampler.h delete mode 100644 src/sample-util.c delete mode 100644 src/sample-util.h delete mode 100644 src/sample.c delete mode 100644 src/sample.h delete mode 100644 src/sconv-s16be.c delete mode 100644 src/sconv-s16be.h delete mode 100644 src/sconv-s16le.c delete mode 100644 src/sconv-s16le.h delete mode 100644 src/sconv.c delete mode 100644 src/sconv.h delete mode 100644 src/sink-input.c delete mode 100644 src/sink-input.h delete mode 100644 src/sink.c delete mode 100644 src/sink.h delete mode 100644 src/sioman.c delete mode 100644 src/sioman.h delete mode 100644 src/socket-client.c delete mode 100644 src/socket-client.h delete mode 100644 src/socket-server.c delete mode 100644 src/socket-server.h delete mode 100644 src/socket-util.c delete mode 100644 src/socket-util.h delete mode 100644 src/source-output.c delete mode 100644 src/source-output.h delete mode 100644 src/source.c delete mode 100644 src/source.h delete mode 100644 src/strbuf.c delete mode 100644 src/strbuf.h delete mode 100644 src/tagstruct.c delete mode 100644 src/tagstruct.h delete mode 100644 src/tokenizer.c delete mode 100644 src/tokenizer.h delete mode 100644 src/util.c delete mode 100644 src/util.h diff --git a/polyp/Makefile.am b/polyp/Makefile.am new file mode 100644 index 00000000..c4da7342 --- /dev/null +++ b/polyp/Makefile.am @@ -0,0 +1,363 @@ +# $Id$ +# +# This file is part of polypaudio. +# +# polypaudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# polypaudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with polypaudio; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + +AM_CFLAGS=-ansi -D_GNU_SOURCE + +EXTRA_DIST = polypaudio.run depmod.py +bin_PROGRAMS = polypaudio pacat pactl +noinst_PROGRAMS = pacat-simple parec-simple + +pkginclude_HEADERS=polyplib.h \ + polyplib-def.h \ + polyplib-simple.h \ + polyplib-error.h \ + mainloop-api.h \ + mainloop.h \ + mainloop-signal.h \ + sample.h + +pkglib_LTLIBRARIES=libiochannel.la \ + libsocket-server.la \ + libsocket-client.la \ + libpstream.la \ + libpacket.la \ + liboss-util.la \ + libalsa-util.la \ + libioline.la \ + libcli.la \ + libprotocol-cli.la \ + libtagstruct.la \ + libpstream-util.la \ + libpdispatch.la \ + libauthkey.la \ + libsocket-util.la \ + libprotocol-simple.la \ + libprotocol-esound.la \ + libprotocol-native.la \ + module-cli.la \ + module-cli-protocol-tcp.la \ + module-cli-protocol-unix.la \ + module-pipe-sink.la \ + module-alsa-sink.la \ + module-alsa-source.la \ + module-oss.la \ + module-oss-mmap.la \ + module-simple-protocol-tcp.la \ + module-simple-protocol-unix.la \ + module-esound-protocol-tcp.la \ + module-esound-protocol-unix.la \ + module-native-protocol-tcp.la \ + module-native-protocol-unix.la + +lib_LTLIBRARIES=libpolyp.la \ + libpolyp-simple.la \ + libpolyp-error.la \ + libpolyp-mainloop.la + +polypaudio_SOURCES = idxset.c idxset.h \ + queue.c queue.h \ + strbuf.c strbuf.h \ + main.c \ + mainloop.c mainloop.h \ + memblock.c memblock.h \ + sample.c sample.h \ + sample-util.c sample-util.h \ + memblockq.c memblockq.h \ + client.c client.h \ + core.c core.h \ + source-output.c source-output.h \ + sink-input.c sink-input.h \ + source.c source.h \ + sink.c sink.h \ + module.c module.h \ + mainloop-signal.c mainloop-signal.h \ + mainloop-api.c mainloop-api.h \ + util.c util.h \ + hashmap.c hashmap.h \ + namereg.c namereg.h \ + sconv.c sconv.h \ + resampler.c resampler.h \ + endianmacros.h \ + memchunk.c memchunk.h \ + sconv-s16le.c sconv-s16le.h \ + sconv-s16be.c sconv-s16be.h \ + sioman.c sioman.h \ + modargs.c modargs.h \ + cmdline.c cmdline.h \ + cli-command.c cli-command.h \ + clitext.c clitext.h \ + tokenizer.c tokenizer.h \ + dynarray.c dynarray.h + +polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) +polypaudio_INCLUDES = $(INCLTDL) +polypaudio_LDADD = $(LIBLTDL) $(LIBSAMPLERATE_LIBS) +polypaudio_LDFLAGS=-export-dynamic + +libprotocol_simple_la_SOURCES = protocol-simple.c protocol-simple.h +libprotocol_simple_la_LDFLAGS = -avoid-version +libprotocol_simple_la_LIBADD = libsocket-server.la libiochannel.la + +libsocket_server_la_SOURCES = socket-server.c socket-server.h +libsocket_server_la_LDFLAGS = -avoid-version +libsocket_server_la_LIBADD = libiochannel.la libsocket-util.la + +libsocket_client_la_SOURCES = socket-client.c socket-client.h +libsocket_client_la_LDFLAGS = -avoid-version +libsocket_client_la_LIBADD = libiochannel.la libsocket-util.la + +libpstream_la_SOURCES = pstream.c pstream.h +libpstream_la_LDFLAGS = -avoid-version +libpstream_la_LIBADD = libpacket.la libiochannel.la + +libpstream_util_la_SOURCES = pstream-util.c pstream-util.h +libpstream_util_la_LDFLAGS = -avoid-version +libpstream_util_la_LIBADD = libpacket.la libpstream.la libtagstruct.la + +libpdispatch_la_SOURCES = pdispatch.c pdispatch.h +libpdispatch_la_LDFLAGS = -avoid-version +libpdispatch_la_LIBADD = libtagstruct.la + +libiochannel_la_SOURCES = iochannel.c iochannel.h +libiochannel_la_LDFLAGS = -avoid-version +libiochannel_la_LIBADD = libsocket-util.la + +libpacket_la_SOURCES = packet.c packet.h +libpacket_la_LDFLAGS = -avoid-version + +liboss_util_la_SOURCES = oss-util.c oss-util.h +liboss_util_la_LDFLAGS = -avoid-version + +libalsa_util_la_SOURCES = alsa-util.c alsa-util.h +libalsa_util_la_LDFLAGS = -avoid-version +libalsa_util_la_LIBADD = $(ASOUNDLIB_LIBS) +libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) + +libioline_la_SOURCES = ioline.c ioline.h +libioline_la_LDFLAGS = -avoid-version +libioline_la_LIBADD = libiochannel.la + +libcli_la_SOURCES = cli.c cli.h +libcli_la_LDFLAGS = -avoid-version +libcli_la_LIBADD = libiochannel.la libioline.la + +libprotocol_cli_la_SOURCES = protocol-cli.c protocol-cli.h +libprotocol_cli_la_LDFLAGS = -avoid-version +libprotocol_cli_la_LIBADD = libsocket-server.la libiochannel.la libcli.la + +libprotocol_native_la_SOURCES = protocol-native.c protocol-native.h native-common.h +libprotocol_native_la_LDFLAGS = -avoid-version +libprotocol_native_la_LIBADD = libsocket-server.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la + +libtagstruct_la_SOURCES = tagstruct.c tagstruct.h +libtagstruct_la_LDFLAGS = -avoid-version + +libprotocol_esound_la_SOURCES = protocol-esound.c protocol-esound.h esound.h +libprotocol_esound_la_LDFLAGS = -avoid-version +libprotocol_esound_la_LIBADD = libsocket-server.la libiochannel.la libauthkey.la + +libauthkey_la_SOURCES = authkey.c authkey.h +libauthkey_la_LDFLAGS = -avoid-version + +libsocket_util_la_SOURCES = socket-util.c socket-util.h +libsocket_util_la_LDFLAGS = -avoid-version + +module_simple_protocol_tcp_la_SOURCES = module-protocol-stub.c +module_simple_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS) +module_simple_protocol_tcp_la_LDFLAGS = -module -avoid-version +module_simple_protocol_tcp_la_LIBADD = libprotocol-simple.la libsocket-server.la + +module_simple_protocol_unix_la_SOURCES = module-protocol-stub.c +module_simple_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS) +module_simple_protocol_unix_la_LDFLAGS = -module -avoid-version +module_simple_protocol_unix_la_LIBADD = libprotocol-simple.la libsocket-server.la libsocket-util.la + +module_cli_protocol_tcp_la_SOURCES = module-protocol-stub.c +module_cli_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS) +module_cli_protocol_tcp_la_LDFLAGS = -module -avoid-version +module_cli_protocol_tcp_la_LIBADD = libprotocol-cli.la libsocket-server.la + +module_cli_protocol_unix_la_SOURCES = module-protocol-stub.c +module_cli_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS) +module_cli_protocol_unix_la_LDFLAGS = -module -avoid-version +module_cli_protocol_unix_la_LIBADD = libprotocol-cli.la libsocket-server.la libsocket-util.la + +module_native_protocol_tcp_la_SOURCES = module-protocol-stub.c +module_native_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS) +module_native_protocol_tcp_la_LDFLAGS = -module -avoid-version +module_native_protocol_tcp_la_LIBADD = libprotocol-native.la libsocket-server.la + +module_native_protocol_unix_la_SOURCES = module-protocol-stub.c +module_native_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS) +module_native_protocol_unix_la_LDFLAGS = -module -avoid-version +module_native_protocol_unix_la_LIBADD = libprotocol-native.la libsocket-server.la libsocket-util.la + +module_esound_protocol_tcp_la_SOURCES = module-protocol-stub.c +module_esound_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS) +module_esound_protocol_tcp_la_LDFLAGS = -module -avoid-version +module_esound_protocol_tcp_la_LIBADD = libprotocol-esound.la libsocket-server.la + +module_esound_protocol_unix_la_SOURCES = module-protocol-stub.c +module_esound_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS) +module_esound_protocol_unix_la_LDFLAGS = -module -avoid-version +module_esound_protocol_unix_la_LIBADD = libprotocol-esound.la libsocket-server.la libsocket-util.la + +module_pipe_sink_la_SOURCES = module-pipe-sink.c +module_pipe_sink_la_LDFLAGS = -module -avoid-version +module_pipe_sink_la_LIBADD = libiochannel.la + +module_alsa_sink_la_SOURCES = module-alsa-sink.c +module_alsa_sink_la_LDFLAGS = -module -avoid-version +module_alsa_sink_la_LIBADD = $(ASOUNDLIB_LIBS) libalsa-util.la +module_alsa_sink_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) + +module_alsa_source_la_SOURCES = module-alsa-source.c +module_alsa_source_la_LDFLAGS = -module -avoid-version +module_alsa_source_la_LIBADD = $(ASOUNDLIB_LIBS) libalsa-util.la +module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) + +module_oss_la_SOURCES = module-oss.c +module_oss_la_LDFLAGS = -module -avoid-version +module_oss_la_LIBADD = libiochannel.la liboss-util.la + +module_oss_mmap_la_SOURCES = module-oss-mmap.c +module_oss_mmap_la_LDFLAGS = -module -avoid-version +module_oss_mmap_la_LIBADD = liboss-util.la + +module_cli_la_SOURCES = module-cli.c +module_cli_la_LDFLAGS = -module -avoid-version +module_cli_la_LIBADD = libcli.la libiochannel.la + +libpolyp_la_SOURCES = polyplib.c polyplib.h \ + polyplib-def.h \ + tagstruct.c tagstruct.h \ + iochannel.c iochannel.h \ + pstream.c pstream.h \ + pstream-util.c pstream-util.h \ + pdispatch.c pdispatch.h \ + mainloop-api.c mainloop-api.h \ + idxset.c idxset.h \ + util.c util.h \ + memblock.c memblock.h \ + socket-client.c socket-client.h \ + packet.c packet.h \ + queue.c queue.h \ + dynarray.c dynarray.h \ + memchunk.c memchunk.h \ + authkey.c authkey.h \ + socket-util.c socket-util.h \ + native-common.h +libpolyp_la_CFLAGS = $(AM_CFLAGS) + +libpolyp_mainloop_la_SOURCES = mainloop-api.h mainloop-api.c \ + mainloop.c mainloop.h \ + mainloop-signal.c mainloop-signal.h +libpolyp_mainloop_la_CFLAGS = $(AM_CFLAGS) + +libpolyp_error_la_SOURCES = polyplib-error.c polyplib-error.h +libpolyp_error_la_CFLAGS = $(AM_CFLAGS) + +libpolyp_simple_la_SOURCES = polyplib-simple.c polyplib-simple.h +libpolyp_simple_la_CFLAGS = $(AM_CFLAGS) +libpolyp_simple_la_LIBADD = libpolyp.la libpolyp-mainloop.la + +pacat_SOURCES = pacat.c +pacat_LDADD = libpolyp.la libpolyp-error.la libpolyp-mainloop.la +pacat_CFLAGS = $(AM_CFLAGS) + +pactl_SOURCES = pactl.c +pactl_LDADD = libpolyp.la libpolyp-error.la libpolyp-mainloop.la +pactl_CFLAGS = $(AM_CFLAGS) + +pacat_simple_SOURCES = pacat-simple.c +pacat_simple_LDADD = libpolyp-simple.la libpolyp-error.la +pacat_simple_CFLAGS = $(AM_CFLAGS) + +parec_simple_SOURCES = parec-simple.c +parec_simple_LDADD = libpolyp-simple.la libpolyp-error.la +parec_simple_CFLAGS = $(AM_CFLAGS) + +if BUILD_LIBPOLYPCORE + +pkginclude_HEADERS+=cli-command.h\ + client.h \ + core.h \ + dynarray.h \ + endianmacros.h \ + hashmap.h \ + idxset.h \ + iochannel.h \ + memblock.h \ + memblockq.h \ + memchunk.h \ + modargs.h \ + module.h \ + namereg.h \ + queue.h \ + resampler.h \ + sample-util.h \ + sink.h \ + sink-input.h \ + sioman.h \ + socket-server.h \ + socket-client.h \ + socket-util.h \ + source.h \ + source-output.h \ + strbuf.h \ + tokenizer.h \ + tagstruct.h \ + util.h + +lib_LTLIBRARIES+= libpolypcore.la + +libpolypcore_la_SOURCES = idxset.c idxset.h \ + queue.c queue.h \ + strbuf.c strbuf.h \ + mainloop.c mainloop.h \ + memblock.c memblock.h \ + sample.c sample.h \ + sample-util.c sample-util.h \ + memblockq.c memblockq.h \ + client.c client.h \ + core.c core.h \ + source-output.c source-output.h \ + sink-input.c sink-input.h \ + source.c source.h \ + sink.c sink.h \ + module.c module.h \ + mainloop-signal.c mainloop-signal.h \ + mainloop-api.c mainloop-api.h \ + util.c util.h \ + hashmap.c hashmap.h \ + namereg.c namereg.h \ + sconv.c sconv.h \ + resampler.c resampler.h \ + endianmacros.h \ + memchunk.c memchunk.h \ + sconv-s16le.c sconv-s16le.h \ + sconv-s16be.c sconv-s16be.h \ + sioman.c sioman.h \ + modargs.c modargs.h \ + cli-command.c cli-command.h \ + clitext.c clitext.h \ + tokenizer.c tokenizer.h \ + dynarray.c dynarray.h + +endif diff --git a/polyp/alsa-util.c b/polyp/alsa-util.c new file mode 100644 index 00000000..7f266df5 --- /dev/null +++ b/polyp/alsa-util.c @@ -0,0 +1,98 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "alsa-util.h" +#include "sample.h" + +int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, struct pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *buffer_size) { + int ret = 0; + snd_pcm_hw_params_t *hwparams = NULL; + static const snd_pcm_format_t format_trans[] = { + [PA_SAMPLE_U8] = SND_PCM_FORMAT_U8, + [PA_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW, + [PA_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW, + [PA_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE, + [PA_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE, + [PA_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE, + [PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE, + }; + + if (snd_pcm_hw_params_malloc(&hwparams) < 0 || + snd_pcm_hw_params_any(pcm_handle, hwparams) < 0 || + snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 || + snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[ss->format]) < 0 || + snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &ss->rate, NULL) < 0 || + snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss->channels) < 0 || + snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, periods, NULL) < 0 || + snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, buffer_size) < 0 || + snd_pcm_hw_params(pcm_handle, hwparams) < 0) { + ret = -1; + } + + if (hwparams) + snd_pcm_hw_params_free(hwparams); + return ret; +} + +int pa_create_io_sources(snd_pcm_t *pcm_handle, struct pa_mainloop_api* m, void ***io_sources, unsigned *n_io_sources, void (*cb)(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata) { + unsigned i; + struct pollfd *pfds, *ppfd; + void **ios; + assert(pcm_handle && m && io_sources && n_io_sources && cb); + + *n_io_sources = snd_pcm_poll_descriptors_count(pcm_handle); + + pfds = malloc(sizeof(struct pollfd) * *n_io_sources); + assert(pfds); + if (snd_pcm_poll_descriptors(pcm_handle, pfds, *n_io_sources) < 0) { + free(pfds); + return -1; + } + + *io_sources = malloc(sizeof(void*) * *n_io_sources); + assert(io_sources); + + for (i = 0, ios = *io_sources, ppfd = pfds; i < *n_io_sources; i++, ios++, ppfd++) { + *ios = m->source_io(m, ppfd->fd, + ((ppfd->events & POLLIN) ? PA_MAINLOOP_API_IO_EVENT_INPUT : 0) | + ((ppfd->events & POLLOUT) ? PA_MAINLOOP_API_IO_EVENT_OUTPUT : 0), cb, userdata); + assert(*ios); + } + + free(pfds); + return 0; +} + +void pa_free_io_sources(struct pa_mainloop_api* m, void **io_sources, unsigned n_io_sources) { + unsigned i; + void **ios; + assert(m && io_sources); + + for (ios = io_sources, i = 0; i < n_io_sources; i++, ios++) + m->cancel_io(m, *ios); + free(io_sources); +} diff --git a/polyp/alsa-util.h b/polyp/alsa-util.h new file mode 100644 index 00000000..03e427d9 --- /dev/null +++ b/polyp/alsa-util.h @@ -0,0 +1,35 @@ +#ifndef fooalsautilhfoo +#define fooalsautilhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "sample.h" +#include "mainloop-api.h" + +int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, struct pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *buffer_size); + +int pa_create_io_sources(snd_pcm_t *pcm_handle, struct pa_mainloop_api *m, void ***io_sources, unsigned *n_io_sources, void (*cb)(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata); +void pa_free_io_sources(struct pa_mainloop_api* m, void **io_sources, unsigned n_io_sources); + +#endif diff --git a/polyp/authkey.c b/polyp/authkey.c new file mode 100644 index 00000000..3180b8fd --- /dev/null +++ b/polyp/authkey.c @@ -0,0 +1,151 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "authkey.h" +#include "util.h" + +#define RANDOM_DEVICE "/dev/urandom" + +static int load(const char *fn, void *data, size_t length) { + int fd = -1, ret = -1; + ssize_t r; + + assert(fn && data && length); + + if ((fd = open(fn, O_RDONLY)) < 0) + goto finish; + + if ((r = pa_loop_read(fd, data, length)) < 0 || (size_t) r != length) { + ret = -2; + goto finish; + } + + ret = 0; + +finish: + if (fd >= 0) + close(fd); + + return ret; +} + +static int generate(const char *fn, void *data, size_t length) { + int fd = -1, random_fd = -1, ret = -1; + ssize_t r; + assert(fn && data && length); + + if ((fd = open(fn, O_WRONLY|O_EXCL|O_CREAT, S_IRUSR | S_IWUSR)) < 0) + goto finish; + + if ((random_fd = open(RANDOM_DEVICE, O_RDONLY)) >= 0) { + + if ((r = pa_loop_read(random_fd, data, length)) < 0 || (size_t) r != length) { + ret = -2; + goto finish; + } + + } else { + uint8_t *p; + size_t l; + fprintf(stderr, "WARNING: Failed to open entropy device '"RANDOM_DEVICE"': %s, falling back to unsecure pseudo RNG.\n", strerror(errno)); + + srandom(time(NULL)); + + for (p = data, l = length; l > 0; p++, l--) + *p = (uint8_t) random(); + } + + if ((r = pa_loop_write(fd, data, length)) < 0 || (size_t) r != length) { + ret = -2; + goto finish; + } + + ret = 0; + +finish: + if (fd >= 0) { + if (ret != 0) + unlink(fn); + close(fd); + } + if (random_fd >= 0) + close(random_fd); + + return ret; +} + +int pa_authkey_load(const char *path, void *data, size_t length) { + int ret, i; + + assert(path && data && length); + + for (i = 0; i < 10; i++) { + if ((ret = load(path, data, length)) < 0) + if (ret == -1 && errno == ENOENT) + if ((ret = generate(path, data, length)) < 0) + if (ret == -1 && errno == EEXIST) + continue; + break; + } + + if (ret < 0) + fprintf(stderr, "Failed to load authorization key '%s': %s\n", path, (ret == -1) ? strerror(errno) : "file corrupt"); + + return ret; +} + +int pa_authkey_load_from_home(const char *fn, void *data, size_t length) { + char *home; + char path[PATH_MAX]; + + assert(fn && data && length); + + if (!(home = getenv("HOME"))) + return -2; + + snprintf(path, sizeof(path), "%s/%s", home, fn); + + return pa_authkey_load(path, data, length); +} + +int pa_authkey_load_auto(const char *fn, void *data, size_t length) { + assert(fn && data && length); + + if (*fn == '/') + return pa_authkey_load(fn, data, length); + else + return pa_authkey_load_from_home(fn, data, length); +} diff --git a/polyp/authkey.h b/polyp/authkey.h new file mode 100644 index 00000000..acdcc24d --- /dev/null +++ b/polyp/authkey.h @@ -0,0 +1,31 @@ +#ifndef fooauthkeyhfoo +#define fooauthkeyhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +int pa_authkey_load(const char *path, void *data, size_t len); +int pa_authkey_load_from_home(const char *fn, void *data, size_t length); +int pa_authkey_load_auto(const char *fn, void *data, size_t length); + +#endif diff --git a/polyp/cli-command.c b/polyp/cli-command.c new file mode 100644 index 00000000..f3a2f8a0 --- /dev/null +++ b/polyp/cli-command.c @@ -0,0 +1,557 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#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 "clitext.h" + +struct command { + const char *name; + int (*proc) (struct pa_core *c, struct pa_tokenizer*t, struct pa_strbuf *buf, int *fail, int *verbose); + const char *help; + unsigned args; +}; + +static int pa_cli_command_exit(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_help(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_modules(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_clients(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_sinks(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_sources(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_sink_inputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_source_outputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_stat(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_info(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_unload(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_sink_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_sink_input_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_sink_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_source_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_kill_client(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_kill_sink_input(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); +static int pa_cli_command_kill_source_output(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); + +static const struct command commands[] = { + { "exit", pa_cli_command_exit, "Terminate the daemon", 1 }, + { "help", pa_cli_command_help, "Show this help", 1 }, + { "modules", pa_cli_command_modules, "List loaded modules", 1 }, + { "sinks", pa_cli_command_sinks, "List loaded sinks", 1 }, + { "sources", pa_cli_command_sources, "List loaded sources", 1 }, + { "clients", pa_cli_command_clients, "List loaded clients", 1 }, + { "sink_inputs", pa_cli_command_sink_inputs, "List sink inputs", 1 }, + { "source_outputs", pa_cli_command_source_outputs, "List source outputs", 1 }, + { "stat", pa_cli_command_stat, "Show memory block statistics", 1 }, + { "info", pa_cli_command_info, "Show comprehensive status", 1 }, + { "ls", pa_cli_command_info, NULL, 1 }, + { "list", pa_cli_command_info, NULL, 1 }, + { "load", pa_cli_command_load, "Load a module (args: name, arguments)", 3}, + { "unload", pa_cli_command_unload, "Unload a module (args: index)", 2}, + { "sink_volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3}, + { "sink_input_volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index|name, volume)", 3}, + { "sink_default", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2}, + { "source_default", pa_cli_command_source_default, "Set the default source (args: index|name)", 2}, + { "kill_client", pa_cli_command_kill_client, "Kill a client (args: index)", 2}, + { "kill_sink_input", pa_cli_command_kill_sink_input, "Kill a sink input (args: index)", 2}, + { "kill_source_output", pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2}, + { NULL, NULL, NULL, 0 } +}; + +static const char whitespace[] = " \t\n\r"; +static const char linebreak[] = "\n\r"; + +static uint32_t parse_index(const char *n) { + long index; + char *x; + index = strtol(n, &x, 0); + if (!x || *x != 0 || index < 0) + return (uint32_t) PA_IDXSET_INVALID; + + return (uint32_t) index; +} + +static int pa_cli_command_exit(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + assert(c && c->mainloop && t); + c->mainloop->quit(c->mainloop, 0); + return 0; +} + +static int pa_cli_command_help(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + const struct command*command; + assert(c && t && buf); + + pa_strbuf_puts(buf, "Available commands:\n"); + + for (command = commands; command->name; command++) + if (command->help) + pa_strbuf_printf(buf, " %-20s %s\n", command->name, command->help); + return 0; +} + +static int pa_cli_command_modules(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + char *s; + assert(c && t); + s = pa_module_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + free(s); + return 0; +} + +static int pa_cli_command_clients(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + char *s; + assert(c && t); + s = pa_client_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + free(s); + return 0; +} + +static int pa_cli_command_sinks(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + char *s; + assert(c && t); + s = pa_sink_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + free(s); + return 0; +} + +static int pa_cli_command_sources(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + char *s; + assert(c && t); + s = pa_source_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + free(s); + return 0; +} + +static int pa_cli_command_sink_inputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + char *s; + assert(c && t); + s = pa_sink_input_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + free(s); + return 0; +} + +static int pa_cli_command_source_outputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + char *s; + assert(c && t); + s = pa_source_output_list_to_string(c); + assert(s); + pa_strbuf_puts(buf, s); + free(s); + return 0; +} + +static int pa_cli_command_stat(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + assert(c && t); + pa_strbuf_printf(buf, "Memory blocks allocated: %u, total size: %u bytes.\n", pa_memblock_get_count(), pa_memblock_get_total()); + return 0; +} + +static int pa_cli_command_info(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + assert(c && t); + pa_cli_command_stat(c, t, buf, fail, verbose); + pa_cli_command_modules(c, t, buf, fail, verbose); + pa_cli_command_sinks(c, t, buf, fail, verbose); + pa_cli_command_sources(c, t, buf, fail, verbose); + pa_cli_command_clients(c, t, buf, fail, verbose); + pa_cli_command_sink_inputs(c, t, buf, fail, verbose); + pa_cli_command_source_outputs(c, t, buf, fail, verbose); + return 0; +} + +static int pa_cli_command_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + struct pa_module *m; + const char *name; + char txt[256]; + assert(c && t); + + if (!(name = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n"); + return -1; + } + + if (!(m = pa_module_load(c, name, pa_tokenizer_get(t, 2)))) { + pa_strbuf_puts(buf, "Module load failed.\n"); + return -1; + } + + if (*verbose) { + snprintf(txt, sizeof(txt), "Module successfully loaded, index: %u.\n", m->index); + pa_strbuf_puts(buf, txt); + } + return 0; +} + +static int pa_cli_command_unload(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + struct pa_module *m; + uint32_t index; + const char *i; + char *e; + assert(c && t); + + if (!(i = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify the module index.\n"); + return -1; + } + + index = (uint32_t) strtoul(i, &e, 10); + if (*e || !(m = pa_idxset_get_by_index(c->modules, index))) { + pa_strbuf_puts(buf, "Invalid module index.\n"); + return -1; + } + + pa_module_unload_request(c, m); + return 0; +} + +static int pa_cli_command_sink_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + const char *n, *v; + char *x = NULL; + struct pa_sink *sink; + long volume; + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n"); + return -1; + } + + if (!(v = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); + return -1; + } + + volume = strtol(v, &x, 0); + if (!x || *x != 0 || volume < 0) { + pa_strbuf_puts(buf, "Failed to parse volume.\n"); + return -1; + } + + if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) { + pa_strbuf_puts(buf, "No sink found by this name or index.\n"); + return -1; + } + + sink->volume = (uint32_t) volume; + return 0; +} + +static int pa_cli_command_sink_input_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + const char *n, *v; + struct pa_sink_input *si; + long volume; + uint32_t index; + char *x; + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n"); + return -1; + } + + if ((index = parse_index(n)) == PA_IDXSET_INVALID) { + pa_strbuf_puts(buf, "Failed to parse index.\n"); + return -1; + } + + if (!(v = pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); + return -1; + } + + x = NULL; + volume = strtol(v, &x, 0); + if (!x || *x != 0 || volume < 0) { + pa_strbuf_puts(buf, "Failed to parse volume.\n"); + return -1; + } + + if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) index))) { + pa_strbuf_puts(buf, "No sink input found with this index.\n"); + return -1; + } + + si->volume = (uint32_t) volume; + return 0; +} + +static int pa_cli_command_sink_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + const char *n; + struct pa_sink *sink; + assert(c && t); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n"); + return -1; + } + + if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) { + pa_strbuf_puts(buf, "No sink found by this name or index.\n"); + return -1; + } + + c->default_sink_index = sink->index; + return 0; +} + +static int pa_cli_command_source_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + const char *n; + struct pa_source *source; + assert(c && t); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n"); + return -1; + } + + if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) { + pa_strbuf_puts(buf, "No source found by this name or index.\n"); + return -1; + } + + c->default_source_index = source->index; + return 0; +} + +static int pa_cli_command_kill_client(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + const char *n; + struct pa_client *client; + uint32_t index; + assert(c && t); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a client by its index.\n"); + return -1; + } + + if ((index = parse_index(n)) == PA_IDXSET_INVALID) { + pa_strbuf_puts(buf, "Failed to parse index.\n"); + return -1; + } + + if (!(client = pa_idxset_get_by_index(c->clients, index))) { + pa_strbuf_puts(buf, "No client found by this index.\n"); + return -1; + } + + pa_client_kill(client); + return 0; +} + +static int pa_cli_command_kill_sink_input(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + const char *n; + struct pa_sink_input *sink_input; + uint32_t index; + assert(c && t); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n"); + return -1; + } + + if ((index = parse_index(n)) == PA_IDXSET_INVALID) { + pa_strbuf_puts(buf, "Failed to parse index.\n"); + return -1; + } + + if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, index))) { + pa_strbuf_puts(buf, "No sink input found by this index.\n"); + return -1; + } + + pa_sink_input_kill(sink_input); + return 0; +} + +static int pa_cli_command_kill_source_output(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { + const char *n; + struct pa_source_output *source_output; + uint32_t index; + assert(c && t); + + if (!(n = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify a source output by its index.\n"); + return -1; + } + + if ((index = parse_index(n)) == PA_IDXSET_INVALID) { + pa_strbuf_puts(buf, "Failed to parse index.\n"); + return -1; + } + + if (!(source_output = pa_idxset_get_by_index(c->source_outputs, index))) { + pa_strbuf_puts(buf, "No source output found by this index.\n"); + return -1; + } + + pa_source_output_kill(source_output); + return 0; +} + +int pa_cli_command_execute_line(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose) { + const char *cs; + + cs = s+strspn(s, whitespace); + + if (*cs == '#' || !*cs) + return 0; + else if (*cs == '.') { + static const char fail_meta[] = ".fail"; + static const char nofail_meta[] = ".nofail"; + static const char verbose_meta[] = ".verbose"; + static const char noverbose_meta[] = ".noverbose"; + + if (!strcmp(cs, verbose_meta)) + *verbose = 1; + else if (!strcmp(cs, noverbose_meta)) + *verbose = 0; + else if (!strcmp(cs, fail_meta)) + *fail = 1; + else if (!strcmp(cs, nofail_meta)) + *fail = 0; + else { + size_t l; + static const char include_meta[] = ".include"; + l = strcspn(cs, whitespace); + + if (l == sizeof(include_meta)-1 && !strncmp(cs, include_meta, l)) { + const char *filename = cs+l+strspn(cs+l, whitespace); + + if (pa_cli_command_execute_file(c, filename, buf, fail, verbose) < 0) + if (*fail) return -1; + } else { + pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs); + if (*fail) return -1; + } + } + } else { + const struct command*command; + int unknown = 1; + size_t l; + + l = strcspn(cs, whitespace); + + for (command = commands; command->name; command++) + if (strlen(command->name) == l && !strncmp(cs, command->name, l)) { + int ret; + struct pa_tokenizer *t = pa_tokenizer_new(cs, command->args); + assert(t); + ret = command->proc(c, t, buf, fail, verbose); + pa_tokenizer_free(t); + unknown = 0; + + if (ret < 0 && *fail) + return -1; + + break; + } + + if (unknown) { + pa_strbuf_printf(buf, "Unknown command: %s\n", cs); + if (*fail) + return -1; + } + } + + return 0; +} + +int pa_cli_command_execute_file(struct pa_core *c, const char *fn, struct pa_strbuf *buf, int *fail, int *verbose) { + char line[256]; + FILE *f = NULL; + int ret = -1; + assert(c && fn && buf); + + if (!(f = fopen(fn, "r"))) { + pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, strerror(errno)); + if (!*fail) + ret = 0; + goto fail; + } + + if (*verbose) + pa_strbuf_printf(buf, "Executing file: '%s'\n", fn); + + while (fgets(line, sizeof(line), f)) { + char *e = line + strcspn(line, linebreak); + *e = 0; + + if (pa_cli_command_execute_line(c, line, buf, fail, verbose) < 0 && *fail) + goto fail; + } + + if (*verbose) + pa_strbuf_printf(buf, "Executed file: '%s'\n", fn); + + ret = 0; + +fail: + if (f) + fclose(f); + + return ret; +} + +int pa_cli_command_execute(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose) { + const char *p; + assert(c && s && buf && fail && verbose); + + p = s; + while (*p) { + size_t l = strcspn(p, linebreak); + char *line = strndup(p, l); + assert(line); + + if (pa_cli_command_execute_line(c, line, buf, fail, verbose) < 0&& *fail) { + free(line); + return -1; + } + free(line); + + p += l; + p += strspn(p, linebreak); + } + + return 0; +} diff --git a/polyp/cli-command.h b/polyp/cli-command.h new file mode 100644 index 00000000..b3c601ad --- /dev/null +++ b/polyp/cli-command.h @@ -0,0 +1,32 @@ +#ifndef fooclicommandhfoo +#define fooclicommandhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "strbuf.h" +#include "core.h" + +int pa_cli_command_execute_line(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose); +int pa_cli_command_execute_file(struct pa_core *c, const char *fn, struct pa_strbuf *buf, int *fail, int *verbose); +int pa_cli_command_execute(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose); + +#endif diff --git a/polyp/cli.c b/polyp/cli.c new file mode 100644 index 00000000..f2aa5a65 --- /dev/null +++ b/polyp/cli.c @@ -0,0 +1,148 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#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 "clitext.h" +#include "cli-command.h" + +struct pa_cli { + struct pa_core *core; + struct pa_ioline *line; + + void (*eof_callback)(struct pa_cli *c, void *userdata); + void *userdata; + + struct pa_client *client; + + int fail, verbose, kill_requested, defer_kill; +}; + +static void line_callback(struct pa_ioline *line, const char *s, void *userdata); + +static const char prompt[] = ">>> "; + +static void client_kill(struct pa_client *c); + +struct pa_cli* pa_cli_new(struct pa_core *core, struct pa_iochannel *io, struct pa_module *m) { + char cname[256]; + struct pa_cli *c; + assert(io); + + c = malloc(sizeof(struct pa_cli)); + assert(c); + c->core = core; + c->line = pa_ioline_new(io); + assert(c->line); + + c->userdata = NULL; + c->eof_callback = NULL; + + pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); + c->client = pa_client_new(core, "CLI", cname); + assert(c->client); + c->client->kill = client_kill; + c->client->userdata = c; + c->client->owner = m; + + pa_ioline_set_callback(c->line, line_callback, c); + pa_ioline_puts(c->line, "Welcome to polypaudio! Use \"help\" for usage information.\n"); + pa_ioline_puts(c->line, prompt); + + c->fail = c->kill_requested = c->defer_kill = 0; + c->verbose = 1; + + return c; +} + +void pa_cli_free(struct pa_cli *c) { + assert(c); + pa_ioline_free(c->line); + pa_client_free(c->client); + free(c); +} + +static void client_kill(struct pa_client *client) { + struct pa_cli *c; + assert(client && client->userdata); + c = client->userdata; + + fprintf(stderr, "CLI client killed.\n"); + if (c->defer_kill) + c->kill_requested = 1; + else { + if (c->eof_callback) + c->eof_callback(c, c->userdata); + } +} + +static void line_callback(struct pa_ioline *line, const char *s, void *userdata) { + struct pa_strbuf *buf; + struct pa_cli *c = userdata; + char *p; + assert(line && c); + + if (!s) { + fprintf(stderr, "CLI got EOF from user.\n"); + if (c->eof_callback) + c->eof_callback(c, c->userdata); + + return; + } + + buf = pa_strbuf_new(); + assert(buf); + c->defer_kill++; + pa_cli_command_execute_line(c->core, s, buf, &c->fail, &c->verbose); + c->defer_kill--; + pa_ioline_puts(line, p = pa_strbuf_tostring_free(buf)); + free(p); + + if (c->kill_requested) { + if (c->eof_callback) + c->eof_callback(c, c->userdata); + } else + pa_ioline_puts(line, prompt); +} + +void pa_cli_set_eof_callback(struct pa_cli *c, void (*cb)(struct pa_cli*c, void *userdata), void *userdata) { + assert(c && cb); + c->eof_callback = cb; + c->userdata = userdata; +} diff --git a/polyp/cli.h b/polyp/cli.h new file mode 100644 index 00000000..9cfee0d8 --- /dev/null +++ b/polyp/cli.h @@ -0,0 +1,36 @@ +#ifndef fooclihfoo +#define fooclihfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "iochannel.h" +#include "core.h" +#include "module.h" + +struct pa_cli; + +struct pa_cli* pa_cli_new(struct pa_core *core, struct pa_iochannel *io, struct pa_module *m); +void pa_cli_free(struct pa_cli *cli); + +void pa_cli_set_eof_callback(struct pa_cli *cli, void (*cb)(struct pa_cli*c, void *userdata), void *userdata); + +#endif diff --git a/polyp/client.c b/polyp/client.c new file mode 100644 index 00000000..0294c9e2 --- /dev/null +++ b/polyp/client.c @@ -0,0 +1,79 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "client.h" + +struct pa_client *pa_client_new(struct pa_core *core, const char *protocol_name, char *name) { + struct pa_client *c; + int r; + assert(core); + + c = malloc(sizeof(struct pa_client)); + assert(c); + c->name = name ? strdup(name) : NULL; + c->owner = NULL; + c->core = core; + c->protocol_name = protocol_name; + + c->kill = NULL; + c->userdata = NULL; + + r = pa_idxset_put(core->clients, c, &c->index); + assert(c->index != PA_IDXSET_INVALID && r >= 0); + + fprintf(stderr, "client: created %u \"%s\"\n", c->index, c->name); + + return c; +} + +void pa_client_free(struct pa_client *c) { + assert(c && c->core); + + pa_idxset_remove_by_data(c->core->clients, c, NULL); + fprintf(stderr, "client: freed %u \"%s\"\n", c->index, c->name); + free(c->name); + free(c); +} + +void pa_client_kill(struct pa_client *c) { + assert(c); + if (!c->kill) { + fprintf(stderr, "kill() operation not implemented for client %u\n", c->index); + return; + } + + c->kill(c); +} + +void pa_client_rename(struct pa_client *c, const char *name) { + assert(c); + free(c->name); + c->name = name ? strdup(name) : NULL; +} diff --git a/polyp/client.h b/polyp/client.h new file mode 100644 index 00000000..c926208e --- /dev/null +++ b/polyp/client.h @@ -0,0 +1,51 @@ +#ifndef fooclienthfoo +#define fooclienthfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "core.h" +#include "module.h" + +struct pa_client { + uint32_t index; + + struct pa_module *owner; + char *name; + struct pa_core *core; + const char *protocol_name; + + void (*kill)(struct pa_client *c); + void *userdata; +}; + +struct pa_client *pa_client_new(struct pa_core *c, const char *protocol_name, char *name); + +/* This function should be called only by the code that created the client */ +void pa_client_free(struct pa_client *c); + +/* Code that didn't create the client should call this function to + * request destruction of the client */ +void pa_client_kill(struct pa_client *c); + +void pa_client_rename(struct pa_client *c, const char *name); + +#endif diff --git a/polyp/clitext.c b/polyp/clitext.c new file mode 100644 index 00000000..c1b9953b --- /dev/null +++ b/polyp/clitext.c @@ -0,0 +1,203 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "clitext.h" +#include "module.h" +#include "client.h" +#include "sink.h" +#include "source.h" +#include "sink-input.h" +#include "source-output.h" +#include "strbuf.h" +#include "sample-util.h" + +char *pa_module_list_to_string(struct pa_core *c) { + struct pa_strbuf *s; + struct pa_module *m; + uint32_t index = PA_IDXSET_INVALID; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_ncontents(c->modules)); + + for (m = pa_idxset_first(c->modules, &index); m; m = pa_idxset_next(c->modules, &index)) + pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\targument: <%s>\n", m->index, m->name, m->argument); + + return pa_strbuf_tostring_free(s); +} + +char *pa_client_list_to_string(struct pa_core *c) { + struct pa_strbuf *s; + struct pa_client *client; + uint32_t index = PA_IDXSET_INVALID; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u client(s).\n", pa_idxset_ncontents(c->clients)); + + for (client = pa_idxset_first(c->clients, &index); client; client = pa_idxset_next(c->clients, &index)) { + pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\tprotocol_name: <%s>\n", client->index, client->name, client->protocol_name); + + if (client->owner) + pa_strbuf_printf(s, "\towner module: <%u>\n", client->owner->index); + } + + return pa_strbuf_tostring_free(s); +} + +char *pa_sink_list_to_string(struct pa_core *c) { + struct pa_strbuf *s; + struct pa_sink *sink, *default_sink; + uint32_t index = PA_IDXSET_INVALID; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_ncontents(c->sinks)); + + default_sink = pa_sink_get_default(c); + + for (sink = pa_idxset_first(c->sinks, &index); sink; sink = pa_idxset_next(c->sinks, &index)) { + char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH]; + pa_sample_snprint(ss, sizeof(ss), &sink->sample_spec); + assert(sink->monitor_source); + pa_strbuf_printf( + s, + " %c index: %u\n\tname: <%s>\n\tvolume: <0x%04x>\n\tlatency: <%u usec>\n\tmonitor_source: <%u>\n\tsample_spec: <%s>\n", + sink == default_sink ? '*' : ' ', + sink->index, sink->name, + (unsigned) sink->volume, + pa_sink_get_latency(sink), + sink->monitor_source->index, + ss); + + if (sink->owner) + pa_strbuf_printf(s, "\towner module: <%u>\n", sink->owner->index); + if (sink->description) + pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description); + } + + return pa_strbuf_tostring_free(s); +} + +char *pa_source_list_to_string(struct pa_core *c) { + struct pa_strbuf *s; + struct pa_source *source, *default_source; + uint32_t index = PA_IDXSET_INVALID; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_ncontents(c->sources)); + + default_source = pa_source_get_default(c); + + for (source = pa_idxset_first(c->sources, &index); source; source = pa_idxset_next(c->sources, &index)) { + char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH]; + pa_sample_snprint(ss, sizeof(ss), &source->sample_spec); + pa_strbuf_printf(s, " %c index: %u\n\tname: <%s>\n\tsample_spec: <%s>\n", source == default_source ? '*' : ' ', source->index, source->name, ss); + + if (source->monitor_of) + pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index); + if (source->owner) + pa_strbuf_printf(s, "\towner module: <%u>\n", source->owner->index); + if (source->description) + pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description); + } + + return pa_strbuf_tostring_free(s); +} + + +char *pa_source_output_list_to_string(struct pa_core *c) { + struct pa_strbuf *s; + struct pa_source_output *o; + uint32_t index = PA_IDXSET_INVALID; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_ncontents(c->source_outputs)); + + for (o = pa_idxset_first(c->source_outputs, &index); o; o = pa_idxset_next(c->source_outputs, &index)) { + char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH]; + pa_sample_snprint(ss, sizeof(ss), &o->sample_spec); + assert(o->source); + pa_strbuf_printf( + s, " index: %u\n\tname: <%s>\n\tsource: <%u>\n\tsample_spec: <%s>\n", + o->index, + o->name, + o->source->index, + ss); + if (o->owner) + pa_strbuf_printf(s, "\towner module: <%u>\n", o->owner->index); + if (o->client) + pa_strbuf_printf(s, "\tclient: <%u>\n", o->client->index); + } + + return pa_strbuf_tostring_free(s); +} + +char *pa_sink_input_list_to_string(struct pa_core *c) { + struct pa_strbuf *s; + struct pa_sink_input *i; + uint32_t index = PA_IDXSET_INVALID; + assert(c); + + s = pa_strbuf_new(); + assert(s); + + pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_ncontents(c->sink_inputs)); + + for (i = pa_idxset_first(c->sink_inputs, &index); i; i = pa_idxset_next(c->sink_inputs, &index)) { + char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH]; + pa_sample_snprint(ss, sizeof(ss), &i->sample_spec); + assert(i->sink); + pa_strbuf_printf( + s, " index: %u\n\tname: <%s>\n\tsink: <%u>\n\tvolume: <0x%04x>\n\tlatency: <%u usec>\n\tsample_spec: <%s>\n", + i->index, + i->name, + i->sink->index, + (unsigned) i->volume, + pa_sink_input_get_latency(i), + ss); + + if (i->owner) + pa_strbuf_printf(s, "\towner module: <%u>\n", i->owner->index); + if (i->client) + pa_strbuf_printf(s, "\tclient: <%u>\n", i->client->index); + } + + return pa_strbuf_tostring_free(s); +} diff --git a/polyp/clitext.h b/polyp/clitext.h new file mode 100644 index 00000000..b1718cb5 --- /dev/null +++ b/polyp/clitext.h @@ -0,0 +1,35 @@ +#ifndef fooclitexthfoo +#define fooclitexthfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "core.h" + +char *pa_sink_input_list_to_string(struct pa_core *c); +char *pa_source_output_list_to_string(struct pa_core *c); +char *pa_sink_list_to_string(struct pa_core *core); +char *pa_source_list_to_string(struct pa_core *c); +char *pa_client_list_to_string(struct pa_core *c); +char *pa_module_list_to_string(struct pa_core *c); + +#endif + diff --git a/polyp/cmdline.c b/polyp/cmdline.c new file mode 100644 index 00000000..a3330145 --- /dev/null +++ b/polyp/cmdline.c @@ -0,0 +1,111 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "cmdline.h" +#include "util.h" +#include "strbuf.h" + +void pa_cmdline_help(const char *argv0) { + const char *e; + + if ((e = strrchr(argv0, '/'))) + e++; + else + e = argv0; + + printf("%s [options]\n" + " -L MODULE Load the specified plugin module with the specified argument\n" + " -F FILE Run the specified script\n" + " -C Open a command line on the running TTY\n" + " -D Daemonize after loading the modules\n" + " -f Dont quit when the startup fails\n" + " -v Verbose startup\n" + " -h Show this help\n", e); +} + +struct pa_cmdline* pa_cmdline_parse(int argc, char * const argv []) { + char c; + struct pa_cmdline *cmdline = NULL; + struct pa_strbuf *buf = NULL; + assert(argc && argv); + + cmdline = malloc(sizeof(struct pa_cmdline)); + assert(cmdline); + cmdline->daemonize = cmdline->help = cmdline->verbose = 0; + cmdline->fail = 1; + + buf = pa_strbuf_new(); + assert(buf); + + while ((c = getopt(argc, argv, "L:F:CDhfv")) != -1) { + switch (c) { + case 'L': + pa_strbuf_printf(buf, "load %s\n", optarg); + break; + case 'F': + pa_strbuf_printf(buf, ".include %s\n", optarg); + break; + case 'C': + pa_strbuf_puts(buf, "load module-cli\n"); + break; + case 'D': + cmdline->daemonize = 1; + break; + case 'h': + cmdline->help = 1; + break; + case 'f': + cmdline->fail = 0; + break; + case 'v': + cmdline->verbose = 0; + break; + default: + goto fail; + } + } + + cmdline->cli_commands = pa_strbuf_tostring_free(buf); + return cmdline; + +fail: + if (cmdline) + pa_cmdline_free(cmdline); + if (buf) + pa_strbuf_free(buf); + return NULL; +} + +void pa_cmdline_free(struct pa_cmdline *cmd) { + assert(cmd); + free(cmd->cli_commands); + free(cmd); +} diff --git a/polyp/cmdline.h b/polyp/cmdline.h new file mode 100644 index 00000000..95ce91de --- /dev/null +++ b/polyp/cmdline.h @@ -0,0 +1,36 @@ +#ifndef foocmdlinehfoo +#define foocmdlinehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + + +struct pa_cmdline { + int daemonize, help, fail, verbose; + char *cli_commands; +}; + +struct pa_cmdline* pa_cmdline_parse(int argc, char * const argv []); +void pa_cmdline_free(struct pa_cmdline *cmd); + +void pa_cmdline_help(const char *argv0); + +#endif diff --git a/polyp/core.c b/polyp/core.c new file mode 100644 index 00000000..dc9525a8 --- /dev/null +++ b/polyp/core.c @@ -0,0 +1,88 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "core.h" +#include "module.h" +#include "sink.h" +#include "source.h" +#include "namereg.h" +#include "util.h" + +struct pa_core* pa_core_new(struct pa_mainloop_api *m) { + struct pa_core* c; + c = malloc(sizeof(struct pa_core)); + assert(c); + + c->mainloop = m; + c->clients = pa_idxset_new(NULL, NULL); + c->sinks = pa_idxset_new(NULL, NULL); + c->sources = pa_idxset_new(NULL, NULL); + c->source_outputs = pa_idxset_new(NULL, NULL); + c->sink_inputs = pa_idxset_new(NULL, NULL); + + c->default_source_index = c->default_sink_index = PA_IDXSET_INVALID; + + c->modules = NULL; + c->namereg = NULL; + + c->default_sample_spec.format = PA_SAMPLE_S16NE; + c->default_sample_spec.rate = 44100; + c->default_sample_spec.channels = 2; + + pa_check_for_sigpipe(); + + return c; +}; + +void pa_core_free(struct pa_core *c) { + assert(c); + + pa_module_unload_all(c); + assert(!c->modules); + + assert(pa_idxset_isempty(c->clients)); + pa_idxset_free(c->clients, NULL, NULL); + + assert(pa_idxset_isempty(c->sinks)); + pa_idxset_free(c->sinks, NULL, NULL); + + assert(pa_idxset_isempty(c->sources)); + pa_idxset_free(c->sources, NULL, NULL); + + assert(pa_idxset_isempty(c->source_outputs)); + pa_idxset_free(c->source_outputs, NULL, NULL); + + assert(pa_idxset_isempty(c->sink_inputs)); + pa_idxset_free(c->sink_inputs, NULL, NULL); + + pa_namereg_free(c); + + free(c); +}; + diff --git a/polyp/core.h b/polyp/core.h new file mode 100644 index 00000000..99d7d76a --- /dev/null +++ b/polyp/core.h @@ -0,0 +1,45 @@ +#ifndef foocorehfoo +#define foocorehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "idxset.h" +#include "hashmap.h" +#include "mainloop-api.h" +#include "sample.h" + +struct pa_core { + struct pa_mainloop_api *mainloop; + + struct pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules; + + struct pa_hashmap *namereg; + + uint32_t default_source_index, default_sink_index; + + struct pa_sample_spec default_sample_spec; +}; + +struct pa_core* pa_core_new(struct pa_mainloop_api *m); +void pa_core_free(struct pa_core*c); + +#endif diff --git a/polyp/depmod.py b/polyp/depmod.py new file mode 100755 index 00000000..85dc66a8 --- /dev/null +++ b/polyp/depmod.py @@ -0,0 +1,73 @@ +#!/usr/bin/python +# $Id$ +# +# This file is part of polypaudio. +# +# polypaudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# polypaudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with polypaudio; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + +import sys, os, string + +exported_symbols = {} +imported_symbols = {} + +for fn in sys.argv[1:]: + f = os.popen("nm '%s'" % fn, "r") + + imported_symbols[fn] = [] + + for line in f: + sym_address = line[:7].strip() + sym_type = line[9].strip() + sym_name = line[11:].strip() + + if sym_name in ('_fini', '_init'): + continue + + if sym_type in ('T', 'B', 'R', 'D' 'G', 'S', 'D'): + if exported_symbols.has_key(sym_name): + sys.stderr.write("CONFLICT: %s defined in both '%s' and '%s'.\n" % (sym_name, fn, exported_symbols[sym_name])) + else: + exported_symbols[sym_name] = fn + elif sym_type in ('U',): + if sym_name[:3] == 'pa_': + imported_symbols[fn].append(sym_name) + + f.close() + +dependencies = {} +unresolved_symbols = {} + +for fn in imported_symbols: + dependencies[fn] = [] + + for sym in imported_symbols[fn]: + if exported_symbols.has_key(sym): + if exported_symbols[sym] not in dependencies[fn]: + dependencies[fn].append(exported_symbols[sym]) + else: + if unresolved_symbols.has_key(sym): + unresolved_symbols[sym].append(fn) + else: + unresolved_symbols[sym] = [fn] + +for sym, files in unresolved_symbols.iteritems(): + print "WARNING: Unresolved symbol '%s' in %s" % (sym, `files`) + +k = dependencies.keys() +k.sort() +for fn in k: + dependencies[fn].sort() + print "%s: %s" % (fn, string.join(dependencies[fn], " ")) diff --git a/polyp/dynarray.c b/polyp/dynarray.c new file mode 100644 index 00000000..24306964 --- /dev/null +++ b/polyp/dynarray.c @@ -0,0 +1,98 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "dynarray.h" + +struct pa_dynarray { + void **data; + unsigned n_allocated, n_entries; +}; + +struct pa_dynarray* pa_dynarray_new(void) { + struct pa_dynarray *a; + a = malloc(sizeof(struct pa_dynarray)); + assert(a); + a->data = NULL; + a->n_entries = 0; + a->n_allocated = 0; + return a; +} + +void pa_dynarray_free(struct pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata) { + unsigned i; + assert(a); + + if (func) + for (i = 0; i < a->n_entries; i++) + if (a->data[i]) + func(a->data[i], userdata); + + free(a->data); + free(a); +} + +void pa_dynarray_put(struct pa_dynarray*a, unsigned i, void *p) { + assert(a); + + if (i >= a->n_allocated) { + unsigned n; + + if (!p) + return; + + n = i+100; + a->data = realloc(a->data, sizeof(void*)*n); + memset(a->data+a->n_allocated, 0, sizeof(void*)*(n-a->n_allocated)); + a->n_allocated = n; + } + + a->data[i] = p; + + if (i >= a->n_entries) + a->n_entries = i+1; +} + +unsigned pa_dynarray_append(struct pa_dynarray*a, void *p) { + unsigned i = a->n_entries; + pa_dynarray_put(a, i, p); + return i; +} + +void *pa_dynarray_get(struct pa_dynarray*a, unsigned i) { + assert(a); + if (i >= a->n_allocated) + return NULL; + assert(a->data); + return a->data[i]; +} + +unsigned pa_dynarray_ncontents(struct pa_dynarray*a) { + assert(a); + return a->n_entries; +} diff --git a/polyp/dynarray.h b/polyp/dynarray.h new file mode 100644 index 00000000..56ec5386 --- /dev/null +++ b/polyp/dynarray.h @@ -0,0 +1,37 @@ +#ifndef foodynarrayhfoo +#define foodynarrayhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +struct pa_dynarray; + +struct pa_dynarray* pa_dynarray_new(void); +void pa_dynarray_free(struct pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata); + +void pa_dynarray_put(struct pa_dynarray*a, unsigned i, void *p); +unsigned pa_dynarray_append(struct pa_dynarray*a, void *p); + +void *pa_dynarray_get(struct pa_dynarray*a, unsigned i); + +unsigned pa_dynarray_ncontents(struct pa_dynarray*a); + +#endif diff --git a/polyp/endianmacros.h b/polyp/endianmacros.h new file mode 100644 index 00000000..cd7b7d83 --- /dev/null +++ b/polyp/endianmacros.h @@ -0,0 +1,62 @@ +#ifndef fooendianmacroshfoo +#define fooendianmacroshfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define INT16_SWAP(x) ((int16_t)(((int16_t) x >> 8) | ((int16_t) x << 8))) +#define UINT16_SWAP(x) ((uint16_t)(((uint16_t) x >> 8) | ((uint16_t) x << 8))) +#define INT32_SWAP(x) ((int32_t)(((int32_t) x >> 24) | ((int32_t) x << 24) | (((int32_t) x & 0xFF00) << 16) | (((int32_t) x) >> 16) & 0xFF00)) +#define UINT32_SWAP(x) ((uint32_t)(((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 16) | (((uint32_t) x) >> 16) & 0xFF00)) + +#ifdef WORDS_BIGENDIAN + #define INT16_FROM_LE(x) INT16_SWAP(x) + #define INT16_FROM_BE(x) ((int16_t)(x)) + #define INT16_TO_LE(x) INT16_SWAP(x) + #define INT16_TO_BE(x) ((int16_t)(x)) + + #define UINT16_FROM_LE(x) UINT16_SWAP(x) + #define UINT16_FROM_BE(x) ((uint16_t)(x)) + #define INT32_FROM_LE(x) INT32_SWAP(x) + #define INT32_FROM_BE(x) ((int32_t)(x)) + #define UINT32_FROM_LE(x) UINT32_SWAP(x) + #define UINT32_FROM_BE(x) ((uint32_t)(x)) +#else + #define INT16_FROM_LE(x) ((int16_t)(x)) + #define INT16_FROM_BE(x) INT16_SWAP(x) + #define INT16_TO_LE(x) ((int16_t)(x)) + #define INT16_TO_BE(x) INT16_SWAP(x) + + #define UINT16_FROM_LE(x) ((uint16_t)(x)) + #define UINT16_FROM_BE(x) UINT16_SWAP(x) + #define INT32_FROM_LE(x) ((int32_t)(x)) + #define INT32_FROM_BE(x) INT32_SWAP(x) + #define UINT32_FROM_LE(x) ((uint32_t)(x)) + #define UINT32_FROM_BE(x) UINT32_SWAP(x) +#endif + +#endif diff --git a/polyp/esound.h b/polyp/esound.h new file mode 100644 index 00000000..01a2962f --- /dev/null +++ b/polyp/esound.h @@ -0,0 +1,213 @@ +#ifndef fooesoundhfoo +#define fooesoundhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* Most of the following is blatantly stolen from esound. */ + + +/* path and name of the default EsounD domain socket */ +#define ESD_UNIX_SOCKET_DIR "/tmp/.esd" +#define ESD_UNIX_SOCKET_NAME "/tmp/.esd/socket" + +/* length of the audio buffer size */ +#define ESD_BUF_SIZE (4 * 1024) +/* maximum size we can write(). Otherwise we might overflow */ +#define ESD_MAX_WRITE_SIZE (21 * 4096) + +/* length of the authorization key, octets */ +#define ESD_KEY_LEN (16) + +/* default port for the EsounD server */ +#define ESD_DEFAULT_PORT (16001) + +/* default sample rate for the EsounD server */ +#define ESD_DEFAULT_RATE (44100) + +/* maximum length of a stream/sample name */ +#define ESD_NAME_MAX (128) + +/* a magic number to identify the relative endianness of a client */ +#define ESD_ENDIAN_KEY ((uint32_t) (('E' << 24) + ('N' << 16) + ('D' << 8) + ('N'))) + +#define ESD_VOLUME_BASE (256) + + +/*************************************/ +/* what can we do to/with the EsounD */ +enum esd_proto { + ESD_PROTO_CONNECT, /* implied on inital client connection */ + + /* pseudo "security" functionality */ + ESD_PROTO_LOCK, /* disable "foreign" client connections */ + ESD_PROTO_UNLOCK, /* enable "foreign" client connections */ + + /* stream functionality: play, record, monitor */ + ESD_PROTO_STREAM_PLAY, /* play all following data as a stream */ + ESD_PROTO_STREAM_REC, /* record data from card as a stream */ + ESD_PROTO_STREAM_MON, /* send mixed buffer output as a stream */ + + /* sample functionality: cache, free, play, loop, EOL, kill */ + ESD_PROTO_SAMPLE_CACHE, /* cache a sample in the server */ + ESD_PROTO_SAMPLE_FREE, /* release a sample in the server */ + ESD_PROTO_SAMPLE_PLAY, /* play a cached sample */ + ESD_PROTO_SAMPLE_LOOP, /* loop a cached sample, til eoloop */ + ESD_PROTO_SAMPLE_STOP, /* stop a looping sample when done */ + ESD_PROTO_SAMPLE_KILL, /* stop the looping sample immed. */ + + /* free and reclaim /dev/dsp functionality */ + ESD_PROTO_STANDBY, /* release /dev/dsp and ignore all data */ + ESD_PROTO_RESUME, /* reclaim /dev/dsp and play sounds again */ + + /* TODO: move these to a more logical place. NOTE: will break the protocol */ + ESD_PROTO_SAMPLE_GETID, /* get the ID for an already-cached sample */ + ESD_PROTO_STREAM_FILT, /* filter mixed buffer output as a stream */ + + /* esd remote management */ + ESD_PROTO_SERVER_INFO, /* get server info (ver, sample rate, format) */ + ESD_PROTO_ALL_INFO, /* get all info (server info, players, samples) */ + ESD_PROTO_SUBSCRIBE, /* track new and removed players and samples */ + ESD_PROTO_UNSUBSCRIBE, /* stop tracking updates */ + + /* esd remote control */ + ESD_PROTO_STREAM_PAN, /* set stream panning */ + ESD_PROTO_SAMPLE_PAN, /* set default sample panning */ + + /* esd status */ + ESD_PROTO_STANDBY_MODE, /* see if server is in standby, autostandby, etc */ + + /* esd latency */ + ESD_PROTO_LATENCY, /* retrieve latency between write()'s and output */ + + ESD_PROTO_MAX /* for bounds checking */ +}; + +/******************/ +/* The EsounD api */ + +/* the properties of a sound buffer are logically or'd */ + +/* bits of stream/sample data */ +#define ESD_MASK_BITS ( 0x000F ) +#define ESD_BITS8 ( 0x0000 ) +#define ESD_BITS16 ( 0x0001 ) + +/* how many interleaved channels of data */ +#define ESD_MASK_CHAN ( 0x00F0 ) +#define ESD_MONO ( 0x0010 ) +#define ESD_STEREO ( 0x0020 ) + +/* whether it's a stream or a sample */ +#define ESD_MASK_MODE ( 0x0F00 ) +#define ESD_STREAM ( 0x0000 ) +#define ESD_SAMPLE ( 0x0100 ) +#define ESD_ADPCM ( 0x0200 ) /* TODO: anyone up for this? =P */ + +/* the function of the stream/sample, and common functions */ +#define ESD_MASK_FUNC ( 0xF000 ) +#define ESD_PLAY ( 0x1000 ) +/* functions for streams only */ +#define ESD_MONITOR ( 0x0000 ) +#define ESD_RECORD ( 0x2000 ) +/* functions for samples only */ +#define ESD_STOP ( 0x0000 ) +#define ESD_LOOP ( 0x2000 ) + +typedef int esd_format_t; +typedef int esd_proto_t; + +/*******************************************************************/ +/* esdmgr.c - functions to implement a "sound manager" for esd */ + +/* structures to retrieve information about streams/samples from the server */ +typedef struct esd_server_info { + + int version; /* server version encoded as an int */ + esd_format_t format; /* magic int with the format info */ + int rate; /* sample rate */ + +} esd_server_info_t; + +typedef struct esd_player_info { + + struct esd_player_info *next; /* point to next entry in list */ + esd_server_info_t *server; /* the server that contains this stream */ + + int source_id; /* either a stream fd or sample id */ + char name[ ESD_NAME_MAX ]; /* name of stream for remote control */ + int rate; /* sample rate */ + int left_vol_scale; /* volume scaling */ + int right_vol_scale; + + esd_format_t format; /* magic int with the format info */ + +} esd_player_info_t; + +typedef struct esd_sample_info { + + struct esd_sample_info *next; /* point to next entry in list */ + esd_server_info_t *server; /* the server that contains this sample */ + + int sample_id; /* either a stream fd or sample id */ + char name[ ESD_NAME_MAX ]; /* name of stream for remote control */ + int rate; /* sample rate */ + int left_vol_scale; /* volume scaling */ + int right_vol_scale; + + esd_format_t format; /* magic int with the format info */ + int length; /* total buffer length */ + +} esd_sample_info_t; + +typedef struct esd_info { + + esd_server_info_t *server; + esd_player_info_t *player_list; + esd_sample_info_t *sample_list; + +} esd_info_t; + +enum esd_standby_mode { + ESM_ERROR, ESM_ON_STANDBY, ESM_ON_AUTOSTANDBY, ESM_RUNNING +}; +typedef int esd_standby_mode_t; + +enum esd_client_state { + ESD_STREAMING_DATA, /* data from here on is streamed data */ + ESD_CACHING_SAMPLE, /* midway through caching a sample */ + ESD_NEEDS_REQDATA, /* more data needed to complere request */ + ESD_NEXT_REQUEST, /* proceed to next request */ + ESD_CLIENT_STATE_MAX /* place holder */ +}; +typedef int esd_client_state_t; + +/* switch endian order for cross platform playing */ +#define swap_endian_32(x) ((x >> 24) | ((x >> 8) & 0xFF00) | (((x & 0xFF00) << 8)) | (x << 24)) +#define maybe_swap_endian_32(c,x) ((c) ? swap_endian_32(x) : x) + +/* the endian key is transferred in binary, if it's read into int, */ +/* and matches ESD_ENDIAN_KEY (ENDN), then the endianness of the */ +/* server and the client match; if it's SWAP_ENDIAN_KEY, swap data */ +#define ESD_SWAP_ENDIAN_KEY ((uint32_t) swap_endian_32(ESD_ENDIAN_KEY)) + + +#endif diff --git a/polyp/hashmap.c b/polyp/hashmap.c new file mode 100644 index 00000000..2c7c92b5 --- /dev/null +++ b/polyp/hashmap.c @@ -0,0 +1,170 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "hashmap.h" +#include "idxset.h" + +struct hashmap_entry { + struct hashmap_entry *next, *previous, *bucket_next, *bucket_previous; + unsigned hash; + const void *key; + void *value; +}; + +struct pa_hashmap { + unsigned size; + struct hashmap_entry **data; + struct hashmap_entry *first_entry; + + unsigned n_entries; + unsigned (*hash_func) (const void *p); + int (*compare_func) (const void*a, const void*b); +}; + +struct pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) { + struct pa_hashmap *h; + h = malloc(sizeof(struct pa_hashmap)); + assert(h); + h->data = malloc(sizeof(struct hashmap_entry*)*(h->size = 1023)); + assert(h->data); + memset(h->data, 0, sizeof(struct hashmap_entry*)*(h->size = 1023)); + h->first_entry = NULL; + h->n_entries = 0; + h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func; + h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func; + return h; +} + +static void remove(struct pa_hashmap *h, struct hashmap_entry *e) { + assert(e); + + if (e->next) + e->next->previous = e->previous; + if (e->previous) + e->previous->next = e->next; + else + h->first_entry = e->next; + + if (e->bucket_next) + e->bucket_next->bucket_previous = e->bucket_previous; + if (e->bucket_previous) + e->bucket_previous->bucket_next = e->bucket_next; + else + h->data[e->hash] = e->bucket_next; + + free(e); + h->n_entries--; +} + +void pa_hashmap_free(struct pa_hashmap*h, void (*free_func)(void *p, void *userdata), void *userdata) { + assert(h); + + while (h->first_entry) { + if (free_func) + free_func(h->first_entry->value, userdata); + remove(h, h->first_entry); + } + + free(h->data); + free(h); +} + +static struct hashmap_entry *get(struct pa_hashmap *h, unsigned hash, const void *key) { + struct hashmap_entry *e; + + for (e = h->data[hash]; e; e = e->bucket_next) + if (h->compare_func(e->key, key) == 0) + return e; + + return NULL; +} + +int pa_hashmap_put(struct pa_hashmap *h, const void *key, void *value) { + struct hashmap_entry *e; + unsigned hash; + assert(h && key); + + hash = h->hash_func(key) % h->size; + + if ((e = get(h, hash, key))) + return -1; + + e = malloc(sizeof(struct hashmap_entry)); + assert(e); + + e->hash = hash; + e->key = key; + e->value = value; + + e->previous = NULL; + e->next = h->first_entry; + if (h->first_entry) + h->first_entry->previous = e; + h->first_entry = e; + + e->bucket_previous = NULL; + e->bucket_next = h->data[hash]; + if (h->data[hash]) + h->data[hash]->bucket_previous = e; + h->data[hash] = e; + + h->n_entries ++; + return 0; +} + +void* pa_hashmap_get(struct pa_hashmap *h, const void *key) { + unsigned hash; + struct hashmap_entry *e; + assert(h && key); + + hash = h->hash_func(key) % h->size; + + if (!(e = get(h, hash, key))) + return NULL; + + return e->value; +} + +int pa_hashmap_remove(struct pa_hashmap *h, const void *key) { + struct hashmap_entry *e; + unsigned hash; + assert(h && key); + + hash = h->hash_func(key) % h->size; + + if (!(e = get(h, hash, key))) + return 1; + + remove(h, e); + return 0; +} + +unsigned pa_hashmap_ncontents(struct pa_hashmap *h) { + return h->n_entries; +} diff --git a/polyp/hashmap.h b/polyp/hashmap.h new file mode 100644 index 00000000..b24e74a5 --- /dev/null +++ b/polyp/hashmap.h @@ -0,0 +1,37 @@ +#ifndef foohashmaphfoo +#define foohashmaphfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +struct pa_hashmap; + +struct pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)); +void pa_hashmap_free(struct pa_hashmap*, void (*free_func)(void *p, void *userdata), void *userdata); + +int pa_hashmap_put(struct pa_hashmap *h, const void *key, void *value); +void* pa_hashmap_get(struct pa_hashmap *h, const void *key); + +int pa_hashmap_remove(struct pa_hashmap *h, const void *key); + +unsigned pa_hashmap_ncontents(struct pa_hashmap *h); + +#endif diff --git a/polyp/idxset.c b/polyp/idxset.c new file mode 100644 index 00000000..cecda6b7 --- /dev/null +++ b/polyp/idxset.c @@ -0,0 +1,397 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "idxset.h" + +struct idxset_entry { + void *data; + uint32_t index; + unsigned hash_value; + + struct idxset_entry *hash_prev, *hash_next; + struct idxset_entry* iterate_prev, *iterate_next; +}; + +struct pa_idxset { + unsigned (*hash_func) (const void *p); + int (*compare_func)(const void *a, const void *b); + + unsigned hash_table_size, n_entries; + struct idxset_entry **hash_table, **array, *iterate_list_head, *iterate_list_tail; + uint32_t index, start_index, array_size; +}; + +unsigned pa_idxset_string_hash_func(const void *p) { + unsigned hash = 0; + const char *c; + + for (c = p; *c; c++) + hash = 31 * hash + *c; + + return hash; +} + +int pa_idxset_string_compare_func(const void *a, const void *b) { + return strcmp(a, b); +} + +unsigned pa_idxset_trivial_hash_func(const void *p) { + return (unsigned) p; +} + +int pa_idxset_trivial_compare_func(const void *a, const void *b) { + return a != b; +} + +struct pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) { + struct pa_idxset *s; + + s = malloc(sizeof(struct pa_idxset)); + assert(s); + s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func; + s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func; + s->hash_table_size = 1023; + s->hash_table = malloc(sizeof(struct idxset_entry*)*s->hash_table_size); + assert(s->hash_table); + memset(s->hash_table, 0, sizeof(struct idxset_entry*)*s->hash_table_size); + s->array = NULL; + s->array_size = 0; + s->index = 0; + s->start_index = 0; + s->n_entries = 0; + + s->iterate_list_head = s->iterate_list_tail = NULL; + + return s; +} + +void pa_idxset_free(struct pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata) { + assert(s); + + if (free_func) { + while (s->iterate_list_head) { + struct idxset_entry *e = s->iterate_list_head; + s->iterate_list_head = s->iterate_list_head->iterate_next; + + if (free_func) + free_func(e->data, userdata); + free(e); + } + } + + free(s->hash_table); + free(s->array); + free(s); +} + +static struct idxset_entry* hash_scan(struct pa_idxset *s, struct idxset_entry* e, void *p) { + assert(p); + + assert(s->compare_func); + for (; e; e = e->hash_next) + if (s->compare_func(e->data, p) == 0) + return e; + + return NULL; +} + +static void extend_array(struct pa_idxset *s, uint32_t index) { + uint32_t i, j, l; + struct idxset_entry** n; + assert(index >= s->start_index); + + if (index < s->start_index + s->array_size) + return; + + for (i = 0; i < s->array_size; i++) + if (s->array[i]) + break; + + l = index - s->start_index - i + 100; + n = malloc(sizeof(struct hash_table_entry*)*l); + assert(n); + memset(n, 0, sizeof(struct hash_table_entry*)*l); + + for (j = 0; j < s->array_size-i; j++) + n[j] = s->array[i+j]; + + free(s->array); + + s->array = n; + s->array_size = l; + s->start_index += i; +} + +static struct idxset_entry** array_index(struct pa_idxset*s, uint32_t index) { + if (index >= s->start_index + s->array_size) + return NULL; + + if (index < s->start_index) + return NULL; + + return s->array + (index - s->start_index); +} + +int pa_idxset_put(struct pa_idxset*s, void *p, uint32_t *index) { + unsigned h; + struct idxset_entry *e, **a; + assert(s && p); + + assert(s->hash_func); + h = s->hash_func(p) % s->hash_table_size; + + assert(s->hash_table); + if ((e = hash_scan(s, s->hash_table[h], p))) { + if (index) + *index = e->index; + + return -1; + } + + e = malloc(sizeof(struct idxset_entry)); + assert(e); + + e->data = p; + e->index = s->index++; + e->hash_value = h; + + /* Insert into hash table */ + e->hash_next = s->hash_table[h]; + e->hash_prev = NULL; + if (s->hash_table[h]) + s->hash_table[h]->hash_prev = e; + s->hash_table[h] = e; + + /* Insert into array */ + extend_array(s, e->index); + a = array_index(s, e->index); + assert(a && !*a); + *a = e; + + /* Insert into linked list */ + e->iterate_next = NULL; + e->iterate_prev = s->iterate_list_tail; + if (s->iterate_list_tail) { + assert(s->iterate_list_head); + s->iterate_list_tail->iterate_next = e; + } else { + assert(!s->iterate_list_head); + s->iterate_list_head = e; + } + s->iterate_list_tail = e; + + s->n_entries++; + assert(s->n_entries >= 1); + + if (index) + *index = e->index; + + return 0; +} + +void* pa_idxset_get_by_index(struct pa_idxset*s, uint32_t index) { + struct idxset_entry **a; + assert(s); + + if (!(a = array_index(s, index))) + return NULL; + + if (!*a) + return NULL; + + return (*a)->data; +} + +void* pa_idxset_get_by_data(struct pa_idxset*s, void *p, uint32_t *index) { + unsigned h; + struct idxset_entry *e; + assert(s && p); + + assert(s->hash_func); + h = s->hash_func(p) % s->hash_table_size; + + assert(s->hash_table); + if (!(e = hash_scan(s, s->hash_table[h], p))) + return NULL; + + if (index) + *index = e->index; + + return e->data; +} + +static void remove_entry(struct pa_idxset *s, struct idxset_entry *e) { + struct idxset_entry **a; + assert(s && e); + + /* Remove from array */ + a = array_index(s, e->index); + assert(a && *a && *a == e); + *a = NULL; + + /* Remove from linked list */ + if (e->iterate_next) + e->iterate_next->iterate_prev = e->iterate_prev; + else + s->iterate_list_tail = e->iterate_prev; + + if (e->iterate_prev) + e->iterate_prev->iterate_next = e->iterate_next; + else + s->iterate_list_head = e->iterate_next; + + /* Remove from hash table */ + if (e->hash_next) + e->hash_next->hash_prev = e->hash_prev; + + if (e->hash_prev) + e->hash_prev->hash_next = e->hash_next; + else + s->hash_table[e->hash_value] = e->hash_next; + + free(e); + + assert(s->n_entries >= 1); + s->n_entries--; +} + +void* pa_idxset_remove_by_index(struct pa_idxset*s, uint32_t index) { + struct idxset_entry **a; + void *data; + + assert(s); + + if (!(a = array_index(s, index))) + return NULL; + + data = (*a)->data; + remove_entry(s, *a); + + return data; +} + +void* pa_idxset_remove_by_data(struct pa_idxset*s, void *data, uint32_t *index) { + struct idxset_entry *e; + unsigned h; + + assert(s->hash_func); + h = s->hash_func(data) % s->hash_table_size; + + assert(s->hash_table); + if (!(e = hash_scan(s, s->hash_table[h], data))) + return NULL; + + data = e->data; + if (index) + *index = e->index; + + remove_entry(s, e); + + return data; +} + +void* pa_idxset_rrobin(struct pa_idxset *s, uint32_t *index) { + struct idxset_entry **a, *e = NULL; + assert(s && index); + + if ((a = array_index(s, *index)) && *a) + e = (*a)->iterate_next; + + if (!e) + e = s->iterate_list_head; + + if (!e) + return NULL; + + *index = e->index; + return e->data; +} + +void* pa_idxset_first(struct pa_idxset *s, uint32_t *index) { + assert(s); + + if (!s->iterate_list_head) + return NULL; + + if (index) + *index = s->iterate_list_head->index; + return s->iterate_list_head->data; +} + +void *pa_idxset_next(struct pa_idxset *s, uint32_t *index) { + struct idxset_entry **a, *e = NULL; + assert(s && index); + + if ((a = array_index(s, *index)) && *a) + e = (*a)->iterate_next; + + if (e) { + *index = e->index; + return e->data; + } else { + *index = PA_IDXSET_INVALID; + return NULL; + } +} + + +int pa_idxset_foreach(struct pa_idxset*s, int (*func)(void *p, uint32_t index, int *del, void*userdata), void *userdata) { + struct idxset_entry *e; + assert(s && func); + + e = s->iterate_list_head; + while (e) { + int del = 0, r; + struct idxset_entry *n = e->iterate_next; + + r = func(e->data, e->index, &del, userdata); + + if (del) + remove_entry(s, e); + + if (r < 0) + return r; + + e = n; + } + + return 0; +} + +unsigned pa_idxset_ncontents(struct pa_idxset*s) { + assert(s); + return s->n_entries; +} + +int pa_idxset_isempty(struct pa_idxset *s) { + assert(s); + return s->n_entries == 0; +} + diff --git a/polyp/idxset.h b/polyp/idxset.h new file mode 100644 index 00000000..f26b03fb --- /dev/null +++ b/polyp/idxset.h @@ -0,0 +1,63 @@ +#ifndef fooidxsethfoo +#define fooidxsethfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#define PA_IDXSET_INVALID ((uint32_t) -1) + +unsigned pa_idxset_trivial_hash_func(const void *p); +int pa_idxset_trivial_compare_func(const void *a, const void *b); + +unsigned pa_idxset_string_hash_func(const void *p); +int pa_idxset_string_compare_func(const void *a, const void *b); + +struct pa_idxset; + +struct pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)); +void pa_idxset_free(struct pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata); + +int pa_idxset_put(struct pa_idxset*s, void *p, uint32_t *index); + +void* pa_idxset_get_by_index(struct pa_idxset*s, uint32_t index); +void* pa_idxset_get_by_data(struct pa_idxset*s, void *p, uint32_t *index); + +void* pa_idxset_remove_by_index(struct pa_idxset*s, uint32_t index); +void* pa_idxset_remove_by_data(struct pa_idxset*s, void *p, uint32_t *index); + +/* This may be used to iterate through all entries. When called with + an invalid index value it returns the first entry, otherwise the + next following. The function is best called with *index = + PA_IDXSET_VALID first. */ +void* pa_idxset_rrobin(struct pa_idxset *s, uint32_t *index); + +/* Return the oldest entry in the idxset */ +void* pa_idxset_first(struct pa_idxset *s, uint32_t *index); +void *pa_idxset_next(struct pa_idxset *s, uint32_t *index); + +int pa_idxset_foreach(struct pa_idxset*s, int (*func)(void *p, uint32_t index, int *del, void*userdata), void *userdata); + +unsigned pa_idxset_ncontents(struct pa_idxset*s); +int pa_idxset_isempty(struct pa_idxset *s); + +#endif diff --git a/polyp/iochannel.c b/polyp/iochannel.c new file mode 100644 index 00000000..69d381f4 --- /dev/null +++ b/polyp/iochannel.c @@ -0,0 +1,222 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "iochannel.h" +#include "util.h" +#include "socket-util.h" + +struct pa_iochannel { + int ifd, ofd; + struct pa_mainloop_api* mainloop; + + void (*callback)(struct pa_iochannel*io, void *userdata); + void*userdata; + + int readable; + int writable; + int hungup; + + int no_close; + + void* input_source, *output_source; +}; + +static void enable_mainloop_sources(struct pa_iochannel *io) { + assert(io); + + if (io->input_source == io->output_source) { + enum pa_mainloop_api_io_events e = PA_MAINLOOP_API_IO_EVENT_NULL; + assert(io->input_source); + + if (!io->readable) + e |= PA_MAINLOOP_API_IO_EVENT_INPUT; + if (!io->writable) + e |= PA_MAINLOOP_API_IO_EVENT_OUTPUT; + + io->mainloop->enable_io(io->mainloop, io->input_source, e); + } else { + if (io->input_source) + io->mainloop->enable_io(io->mainloop, io->input_source, io->readable ? PA_MAINLOOP_API_IO_EVENT_NULL : PA_MAINLOOP_API_IO_EVENT_INPUT); + if (io->output_source) + io->mainloop->enable_io(io->mainloop, io->output_source, io->writable ? PA_MAINLOOP_API_IO_EVENT_NULL : PA_MAINLOOP_API_IO_EVENT_OUTPUT); + } +} + +static void callback(struct pa_mainloop_api* m, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { + struct pa_iochannel *io = userdata; + int changed = 0; + assert(m && fd >= 0 && events && userdata); + + if ((events & PA_MAINLOOP_API_IO_EVENT_HUP) && !io->hungup) { + io->hungup = 1; + changed = 1; + } + + if ((events & PA_MAINLOOP_API_IO_EVENT_INPUT) && !io->readable) { + io->readable = 1; + changed = 1; + assert(id == io->input_source); + } + + if ((events & PA_MAINLOOP_API_IO_EVENT_OUTPUT) && !io->writable) { + io->writable = 1; + changed = 1; + assert(id == io->output_source); + } + + if (changed) { + enable_mainloop_sources(io); + + if (io->callback) + io->callback(io, io->userdata); + } +} + +struct pa_iochannel* pa_iochannel_new(struct pa_mainloop_api*m, int ifd, int ofd) { + struct pa_iochannel *io; + assert(m && (ifd >= 0 || ofd >= 0)); + + io = malloc(sizeof(struct pa_iochannel)); + io->ifd = ifd; + io->ofd = ofd; + io->mainloop = m; + + io->userdata = NULL; + io->callback = NULL; + io->readable = 0; + io->writable = 0; + io->hungup = 0; + io->no_close = 0; + + if (ifd == ofd) { + assert(ifd >= 0); + pa_make_nonblock_fd(io->ifd); + io->input_source = io->output_source = m->source_io(m, ifd, PA_MAINLOOP_API_IO_EVENT_BOTH, callback, io); + } else { + + if (ifd >= 0) { + pa_make_nonblock_fd(io->ifd); + io->input_source = m->source_io(m, ifd, PA_MAINLOOP_API_IO_EVENT_INPUT, callback, io); + } else + io->input_source = NULL; + + if (ofd >= 0) { + pa_make_nonblock_fd(io->ofd); + io->output_source = m->source_io(m, ofd, PA_MAINLOOP_API_IO_EVENT_OUTPUT, callback, io); + } else + io->output_source = NULL; + } + + return io; +} + +void pa_iochannel_free(struct pa_iochannel*io) { + assert(io); + + if (!io->no_close) { + if (io->ifd >= 0) + close(io->ifd); + if (io->ofd >= 0 && io->ofd != io->ifd) + close(io->ofd); + } + + if (io->input_source) + io->mainloop->cancel_io(io->mainloop, io->input_source); + if (io->output_source && (io->output_source != io->input_source)) + io->mainloop->cancel_io(io->mainloop, io->output_source); + + free(io); +} + +int pa_iochannel_is_readable(struct pa_iochannel*io) { + assert(io); + return io->readable; +} + +int pa_iochannel_is_writable(struct pa_iochannel*io) { + assert(io); + return io->writable; +} + +int pa_iochannel_is_hungup(struct pa_iochannel*io) { + assert(io); + return io->hungup; +} + +ssize_t pa_iochannel_write(struct pa_iochannel*io, const void*data, size_t l) { + ssize_t r; + assert(io && data && l && io->ofd >= 0); + + if ((r = write(io->ofd, data, l)) >= 0) { + io->writable = 0; + enable_mainloop_sources(io); + } + + return r; +} + +ssize_t pa_iochannel_read(struct pa_iochannel*io, void*data, size_t l) { + ssize_t r; + + assert(io && data && io->ifd >= 0); + + if ((r = read(io->ifd, data, l)) >= 0) { + io->readable = 0; + enable_mainloop_sources(io); + } + + return r; +} + +void pa_iochannel_set_callback(struct pa_iochannel*io, void (*callback)(struct pa_iochannel*io, void *userdata), void *userdata) { + assert(io); + io->callback = callback; + io->userdata = userdata; +} + +void pa_iochannel_set_noclose(struct pa_iochannel*io, int b) { + assert(io); + io->no_close = b; +} + +void pa_iochannel_socket_peer_to_string(struct pa_iochannel*io, char*s, size_t l) { + assert(io && s && l); + pa_socket_peer_to_string(io->ifd, s, l); +} + +int pa_iochannel_socket_set_rcvbuf(struct pa_iochannel *io, size_t l) { + assert(io); + return pa_socket_set_rcvbuf(io->ifd, l); +} + +int pa_iochannel_socket_set_sndbuf(struct pa_iochannel *io, size_t l) { + assert(io); + return pa_socket_set_sndbuf(io->ofd, l); +} diff --git a/polyp/iochannel.h b/polyp/iochannel.h new file mode 100644 index 00000000..6f5f351c --- /dev/null +++ b/polyp/iochannel.h @@ -0,0 +1,50 @@ +#ifndef fooiochannelhfoo +#define fooiochannelhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include "mainloop-api.h" + +/* It is safe to destroy the calling iochannel object from the callback */ + +struct pa_iochannel; + +struct pa_iochannel* pa_iochannel_new(struct pa_mainloop_api*m, int ifd, int ofd); +void pa_iochannel_free(struct pa_iochannel*io); + +ssize_t pa_iochannel_write(struct pa_iochannel*io, const void*data, size_t l); +ssize_t pa_iochannel_read(struct pa_iochannel*io, void*data, size_t l); + +int pa_iochannel_is_readable(struct pa_iochannel*io); +int pa_iochannel_is_writable(struct pa_iochannel*io); +int pa_iochannel_is_hungup(struct pa_iochannel*io); + +void pa_iochannel_set_noclose(struct pa_iochannel*io, int b); + +void pa_iochannel_set_callback(struct pa_iochannel*io, void (*callback)(struct pa_iochannel*io, void *userdata), void *userdata); + +void pa_iochannel_socket_peer_to_string(struct pa_iochannel*io, char*s, size_t l); +int pa_iochannel_socket_set_rcvbuf(struct pa_iochannel*io, size_t l); +int pa_iochannel_socket_set_sndbuf(struct pa_iochannel*io, size_t l); + +#endif diff --git a/polyp/ioline.c b/polyp/ioline.c new file mode 100644 index 00000000..ff9a03c2 --- /dev/null +++ b/polyp/ioline.c @@ -0,0 +1,220 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "ioline.h" + +#define BUFFER_LIMIT (64*1024) +#define READ_SIZE (1024) + +struct pa_ioline { + struct pa_iochannel *io; + int dead; + + char *wbuf; + size_t wbuf_length, wbuf_index, wbuf_valid_length; + + char *rbuf; + size_t rbuf_length, rbuf_index, rbuf_valid_length; + + void (*callback)(struct pa_ioline*io, const char *s, void *userdata); + void *userdata; +}; + +static void io_callback(struct pa_iochannel*io, void *userdata); +static int do_write(struct pa_ioline *l); + +struct pa_ioline* pa_ioline_new(struct pa_iochannel *io) { + struct pa_ioline *l; + assert(io); + + l = malloc(sizeof(struct pa_ioline)); + assert(l); + l->io = io; + l->dead = 0; + + l->wbuf = NULL; + l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0; + + l->rbuf = NULL; + l->rbuf_length = l->rbuf_index = l->rbuf_valid_length = 0; + + l->callback = NULL; + l->userdata = NULL; + + pa_iochannel_set_callback(io, io_callback, l); + + return l; +} + +void pa_ioline_free(struct pa_ioline *l) { + assert(l); + pa_iochannel_free(l->io); + free(l->wbuf); + free(l->rbuf); + free(l); +} + +void pa_ioline_puts(struct pa_ioline *l, const char *c) { + size_t len; + assert(l && c); + + len = strlen(c); + if (len > BUFFER_LIMIT - l->wbuf_valid_length) + len = BUFFER_LIMIT - l->wbuf_valid_length; + + if (!len) + return; + + if (len > l->wbuf_length - l->wbuf_valid_length) { + size_t n = l->wbuf_valid_length+len; + char *new = malloc(n); + if (l->wbuf) { + memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length); + free(l->wbuf); + } + l->wbuf = new; + l->wbuf_length = n; + l->wbuf_index = 0; + } else if (len > l->wbuf_length - l->wbuf_valid_length - l->wbuf_index) { + memmove(l->wbuf, l->wbuf+l->wbuf_index, l->wbuf_valid_length); + l->wbuf_index = 0; + } + + memcpy(l->wbuf+l->wbuf_index+l->wbuf_valid_length, c, len); + l->wbuf_valid_length += len; + + do_write(l); +} + +void pa_ioline_set_callback(struct pa_ioline*l, void (*callback)(struct pa_ioline*io, const char *s, void *userdata), void *userdata) { + assert(l && callback); + l->callback = callback; + l->userdata = userdata; +} + +static int do_read(struct pa_ioline *l) { + ssize_t r; + size_t m, len; + char *e; + assert(l); + + if (!pa_iochannel_is_readable(l->io)) + return 0; + + len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length; + + if (len < READ_SIZE) { + size_t n = l->rbuf_valid_length+READ_SIZE; + + if (n >= BUFFER_LIMIT) + n = BUFFER_LIMIT; + + if (l->rbuf_length >= n) { + if (l->rbuf_valid_length) + memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length); + } else { + char *new = malloc(n); + if (l->rbuf_valid_length) + memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length); + free(l->rbuf); + l->rbuf = new; + l->rbuf_length = n; + } + + l->rbuf_index = 0; + } + + len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length; + + if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0) + return -1; + + e = memchr(l->rbuf+l->rbuf_index+l->rbuf_valid_length, '\n', r); + l->rbuf_valid_length += r; + + if (!e &&l->rbuf_valid_length >= BUFFER_LIMIT) + e = l->rbuf+BUFFER_LIMIT-1; + + if (e) { + char *p; + + *e = 0; + + p = l->rbuf+l->rbuf_index; + m = strlen(p); + + l->rbuf_index += m+1; + l->rbuf_valid_length -= m+1; + + if (l->rbuf_valid_length == 0) + l->rbuf_index = 0; + + if (l->callback) + l->callback(l, p, l->userdata); + } + + return 0; +} + +static int do_write(struct pa_ioline *l) { + ssize_t r; + assert(l); + + if (!l->wbuf_valid_length || !pa_iochannel_is_writable(l->io)) + return 0; + + if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0) + return -1; + + l->wbuf_valid_length -= r; + if (l->wbuf_valid_length == 0) + l->wbuf_index = 0; + + return 0; +} + +static void io_callback(struct pa_iochannel*io, void *userdata) { + struct pa_ioline *l = userdata; + assert(io && l); + + if (!l->dead && do_write(l) < 0) + goto fail; + + if (!l->dead && do_read(l) < 0) + goto fail; + + return; + +fail: + l->dead = 1; + if (l->callback) + l->callback(l, NULL, l->userdata); +} diff --git a/polyp/ioline.h b/polyp/ioline.h new file mode 100644 index 00000000..5f29a16b --- /dev/null +++ b/polyp/ioline.h @@ -0,0 +1,35 @@ +#ifndef fooiolinehfoo +#define fooiolinehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "iochannel.h" + +struct pa_ioline; + +struct pa_ioline* pa_ioline_new(struct pa_iochannel *io); +void pa_ioline_free(struct pa_ioline *l); + +void pa_ioline_puts(struct pa_ioline *s, const char *c); +void pa_ioline_set_callback(struct pa_ioline*io, void (*callback)(struct pa_ioline*io, const char *s, void *userdata), void *userdata); + +#endif diff --git a/polyp/main.c b/polyp/main.c new file mode 100644 index 00000000..d9967cef --- /dev/null +++ b/polyp/main.c @@ -0,0 +1,182 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "mainloop.h" +#include "module.h" +#include "mainloop-signal.h" +#include "cmdline.h" +#include "cli-command.h" +#include "util.h" +#include "sioman.h" + +static struct pa_mainloop *mainloop; + +static void exit_signal_callback(void *id, int sig, void *userdata) { + struct pa_mainloop_api* m = pa_mainloop_get_api(mainloop); + m->quit(m, 1); + fprintf(stderr, __FILE__": got signal.\n"); +} + +static void aux_signal_callback(void *id, int sig, void *userdata) { + struct pa_core *c = userdata; + assert(c); + pa_module_load(c, sig == SIGUSR1 ? "module-cli" : "module-cli-protocol-unix", NULL); +} + +static void close_pipe(int p[2]) { + if (p[0] != -1) + close(p[0]); + if (p[1] != -1) + close(p[1]); + p[0] = p[1] = -1; +} + +int main(int argc, char *argv[]) { + struct pa_core *c; + struct pa_cmdline *cmdline = NULL; + struct pa_strbuf *buf = NULL; + char *s; + int r, retval = 1; + int daemon_pipe[2] = { -1, -1 }; + + if (!(cmdline = pa_cmdline_parse(argc, argv))) { + fprintf(stderr, __FILE__": failed to parse command line.\n"); + goto finish; + } + + if (cmdline->help) { + pa_cmdline_help(argv[0]); + retval = 0; + goto finish; + } + + if (cmdline->daemonize) { + pid_t child; + + if (pa_stdio_acquire() < 0) { + fprintf(stderr, __FILE__": failed to acquire stdio.\n"); + goto finish; + } + + if (pipe(daemon_pipe) < 0) { + fprintf(stderr, __FILE__": failed to create pipe.\n"); + goto finish; + } + + if ((child = fork()) < 0) { + fprintf(stderr, __FILE__": fork() failed: %s\n", strerror(errno)); + goto finish; + } + + if (child != 0) { + /* Father */ + + close(daemon_pipe[1]); + daemon_pipe[1] = -1; + + if (pa_loop_read(daemon_pipe[0], &retval, sizeof(retval)) != sizeof(retval)) { + fprintf(stderr, __FILE__": read() failed: %s\n", strerror(errno)); + retval = 1; + } + + goto finish; + } + + close(daemon_pipe[0]); + daemon_pipe[0] = -1; + + setsid(); + setpgrp(); + } + + r = lt_dlinit(); + assert(r == 0); + + mainloop = pa_mainloop_new(); + assert(mainloop); + + r = pa_signal_init(pa_mainloop_get_api(mainloop)); + assert(r == 0); + pa_signal_register(SIGINT, exit_signal_callback, NULL); + signal(SIGPIPE, SIG_IGN); + + c = pa_core_new(pa_mainloop_get_api(mainloop)); + assert(c); + + pa_signal_register(SIGUSR1, aux_signal_callback, c); + pa_signal_register(SIGUSR2, aux_signal_callback, c); + + buf = pa_strbuf_new(); + assert(buf); + r = pa_cli_command_execute(c, cmdline->cli_commands, buf, &cmdline->fail, &cmdline->verbose); + fprintf(stderr, s = pa_strbuf_tostring_free(buf)); + free(s); + + if (r < 0 && cmdline->fail) { + fprintf(stderr, __FILE__": failed to initialize daemon.\n"); + if (cmdline->daemonize) + pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); + } else if (!c->modules || pa_idxset_ncontents(c->modules) == 0) { + fprintf(stderr, __FILE__": daemon startup without any loaded modules, refusing to work.\n"); + if (cmdline->daemonize) + pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); + } else { + retval = 0; + if (cmdline->daemonize) + pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); + fprintf(stderr, __FILE__": mainloop entry.\n"); + if (pa_mainloop_run(mainloop, &retval) < 0) + retval = 1; + fprintf(stderr, __FILE__": mainloop exit.\n"); + } + + pa_core_free(c); + + pa_signal_done(); + pa_mainloop_free(mainloop); + + lt_dlexit(); + +finish: + + if (cmdline) + pa_cmdline_free(cmdline); + + close_pipe(daemon_pipe); + + return retval; +} diff --git a/polyp/mainloop-api.c b/polyp/mainloop-api.c new file mode 100644 index 00000000..cce49c06 --- /dev/null +++ b/polyp/mainloop-api.c @@ -0,0 +1,60 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include "mainloop-api.h" + +struct once_info { + void (*callback)(void *userdata); + void *userdata; +}; + +static void once_callback(struct pa_mainloop_api *api, void *id, void *userdata) { + struct once_info *i = userdata; + assert(api && i && i->callback); + i->callback(i->userdata); + assert(api->cancel_fixed); + api->cancel_fixed(api, id); + free(i); +} + +void pa_mainloop_api_once(struct pa_mainloop_api* api, void (*callback)(void *userdata), void *userdata) { + struct once_info *i; + void *id; + assert(api && callback); + + i = malloc(sizeof(struct once_info)); + assert(i); + i->callback = callback; + i->userdata = userdata; + + assert(api->source_fixed); + id = api->source_fixed(api, once_callback, i); + assert(id); + + /* Note: if the mainloop is destroyed before once_callback() was called, some memory is leaked. */ +} + diff --git a/polyp/mainloop-api.h b/polyp/mainloop-api.h new file mode 100644 index 00000000..0228f580 --- /dev/null +++ b/polyp/mainloop-api.h @@ -0,0 +1,65 @@ +#ifndef foomainloopapihfoo +#define foomainloopapihfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +enum pa_mainloop_api_io_events { + PA_MAINLOOP_API_IO_EVENT_NULL = 0, + PA_MAINLOOP_API_IO_EVENT_INPUT = 1, + PA_MAINLOOP_API_IO_EVENT_OUTPUT = 2, + PA_MAINLOOP_API_IO_EVENT_BOTH = 3, + PA_MAINLOOP_API_IO_EVENT_HUP = 4 +}; + +struct pa_mainloop_api { + void *userdata; + + /* IO sources */ + void* (*source_io)(struct pa_mainloop_api*a, int fd, enum pa_mainloop_api_io_events events, void (*callback) (struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata); + void (*enable_io)(struct pa_mainloop_api*a, void* id, enum pa_mainloop_api_io_events events); + void (*cancel_io)(struct pa_mainloop_api*a, void* id); + + /* Fixed sources */ + void* (*source_fixed)(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, void *id, void *userdata), void *userdata); + void (*enable_fixed)(struct pa_mainloop_api*a, void* id, int b); + void (*cancel_fixed)(struct pa_mainloop_api*a, void* id); + + /* Idle sources */ + void* (*source_idle)(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, void *id, void *userdata), void *userdata); + void (*enable_idle)(struct pa_mainloop_api*a, void* id, int b); + void (*cancel_idle)(struct pa_mainloop_api*a, void* id); + + /* Time sources */ + void* (*source_time)(struct pa_mainloop_api*a, const struct timeval *tv, void (*callback) (struct pa_mainloop_api*a, void *id, const struct timeval *tv, void *userdata), void *userdata); + void (*enable_time)(struct pa_mainloop_api*a, void *id, const struct timeval *tv); + void (*cancel_time)(struct pa_mainloop_api*a, void* id); + + /* Exit mainloop */ + void (*quit)(struct pa_mainloop_api*a, int retval); +}; + +void pa_mainloop_api_once(struct pa_mainloop_api*m, void (*callback)(void *userdata), void *userdata); + +#endif diff --git a/polyp/mainloop-signal.c b/polyp/mainloop-signal.c new file mode 100644 index 00000000..642ca5e0 --- /dev/null +++ b/polyp/mainloop-signal.c @@ -0,0 +1,163 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "mainloop-signal.h" +#include "util.h" + +struct signal_info { + int sig; + struct sigaction saved_sigaction; + void (*callback) (void *id, int signal, void *userdata); + void *userdata; + struct signal_info *previous, *next; +}; + +static struct pa_mainloop_api *api = NULL; +static int signal_pipe[2] = { -1, -1 }; +static void* mainloop_source = NULL; +static struct signal_info *signals = NULL; + +static void signal_handler(int sig) { + write(signal_pipe[1], &sig, sizeof(sig)); +} + +static void callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { + assert(a && id && events == PA_MAINLOOP_API_IO_EVENT_INPUT && id == mainloop_source && fd == signal_pipe[0]); + + for (;;) { + ssize_t r; + int sig; + struct signal_info*s; + + if ((r = read(signal_pipe[0], &sig, sizeof(sig))) < 0) { + if (errno == EAGAIN) + return; + + fprintf(stderr, "signal.c: read(): %s\n", strerror(errno)); + return; + } + + if (r != sizeof(sig)) { + fprintf(stderr, "signal.c: short read()\n"); + return; + } + + for (s = signals; s; s = s->next) + if (s->sig == sig) { + assert(s->callback); + s->callback(s, sig, s->userdata); + break; + } + } +} + +int pa_signal_init(struct pa_mainloop_api *a) { + assert(a); + if (pipe(signal_pipe) < 0) { + fprintf(stderr, "pipe() failed: %s\n", strerror(errno)); + return -1; + } + + pa_make_nonblock_fd(signal_pipe[0]); + pa_make_nonblock_fd(signal_pipe[1]); + + api = a; + mainloop_source = api->source_io(api, signal_pipe[0], PA_MAINLOOP_API_IO_EVENT_INPUT, callback, NULL); + assert(mainloop_source); + return 0; +} + +void pa_signal_done(void) { + assert(api && signal_pipe[0] >= 0 && signal_pipe[1] >= 0 && mainloop_source); + + api->cancel_io(api, mainloop_source); + mainloop_source = NULL; + + close(signal_pipe[0]); + close(signal_pipe[1]); + signal_pipe[0] = signal_pipe[1] = -1; + + while (signals) + pa_signal_unregister(signals); + + api = NULL; +} + +void* pa_signal_register(int sig, void (*callback) (void *id, int signal, void *userdata), void *userdata) { + struct signal_info *s = NULL; + struct sigaction sa; + assert(sig > 0 && callback); + + for (s = signals; s; s = s->next) + if (s->sig == sig) + goto fail; + + s = malloc(sizeof(struct signal_info)); + assert(s); + s->sig = sig; + s->callback = callback; + s->userdata = userdata; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = signal_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if (sigaction(sig, &sa, &s->saved_sigaction) < 0) + goto fail; + + s->previous = NULL; + s->next = signals; + signals = s; + + return s; +fail: + if (s) + free(s); + return NULL; +} + +void pa_signal_unregister(void *id) { + struct signal_info *s = id; + assert(s); + + if (s->next) + s->next->previous = s->previous; + if (s->previous) + s->previous->next = s->next; + else + signals = s->next; + + sigaction(s->sig, &s->saved_sigaction, NULL); + free(s); +} diff --git a/polyp/mainloop-signal.h b/polyp/mainloop-signal.h new file mode 100644 index 00000000..8afe9c8d --- /dev/null +++ b/polyp/mainloop-signal.h @@ -0,0 +1,33 @@ +#ifndef foomainloopsignalhfoo +#define foomainloopsignalhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "mainloop-api.h" + +int pa_signal_init(struct pa_mainloop_api *api); +void pa_signal_done(void); + +void* pa_signal_register(int signal, void (*callback) (void *id, int signal, void *userdata), void *userdata); +void pa_signal_unregister(void *id); + +#endif diff --git a/polyp/mainloop.c b/polyp/mainloop.c new file mode 100644 index 00000000..b9eee86d --- /dev/null +++ b/polyp/mainloop.c @@ -0,0 +1,553 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mainloop.h" +#include "util.h" +#include "idxset.h" + +struct mainloop_source_header { + struct pa_mainloop *mainloop; + int dead; +}; + +struct mainloop_source_io { + struct mainloop_source_header header; + + int fd; + enum pa_mainloop_api_io_events events; + void (*callback) (struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata); + void *userdata; + + struct pollfd *pollfd; +}; + +struct mainloop_source_fixed_or_idle { + struct mainloop_source_header header; + int enabled; + + void (*callback)(struct pa_mainloop_api*a, void *id, void *userdata); + void *userdata; +}; + +struct mainloop_source_time { + struct mainloop_source_header header; + int enabled; + + struct timeval timeval; + void (*callback)(struct pa_mainloop_api*a, void *id, const struct timeval*tv, void *userdata); + void *userdata; +}; + +struct pa_mainloop { + struct pa_idxset *io_sources, *fixed_sources, *idle_sources, *time_sources; + int io_sources_scan_dead, fixed_sources_scan_dead, idle_sources_scan_dead, time_sources_scan_dead; + + struct pollfd *pollfds; + unsigned max_pollfds, n_pollfds; + int rebuild_pollfds; + + int quit, running, retval; + struct pa_mainloop_api api; +}; + +static void setup_api(struct pa_mainloop *m); + +struct pa_mainloop *pa_mainloop_new(void) { + struct pa_mainloop *m; + + m = malloc(sizeof(struct pa_mainloop)); + assert(m); + + m->io_sources = pa_idxset_new(NULL, NULL); + m->fixed_sources = pa_idxset_new(NULL, NULL); + m->idle_sources = pa_idxset_new(NULL, NULL); + m->time_sources = pa_idxset_new(NULL, NULL); + + assert(m->io_sources && m->fixed_sources && m->idle_sources && m->time_sources); + + m->io_sources_scan_dead = m->fixed_sources_scan_dead = m->idle_sources_scan_dead = m->time_sources_scan_dead = 0; + + m->pollfds = NULL; + m->max_pollfds = m->n_pollfds = m->rebuild_pollfds = 0; + + m->quit = m->running = m->retval = 0; + + setup_api(m); + + return m; +} + +static int foreach(void *p, uint32_t index, int *del, void*userdata) { + struct mainloop_source_header *h = p; + int *all = userdata; + assert(p && del && all); + + if (*all || h->dead) { + free(h); + *del = 1; + } + + return 0; +}; + +void pa_mainloop_free(struct pa_mainloop* m) { + int all = 1; + assert(m); + pa_idxset_foreach(m->io_sources, foreach, &all); + pa_idxset_foreach(m->fixed_sources, foreach, &all); + pa_idxset_foreach(m->idle_sources, foreach, &all); + pa_idxset_foreach(m->time_sources, foreach, &all); + + pa_idxset_free(m->io_sources, NULL, NULL); + pa_idxset_free(m->fixed_sources, NULL, NULL); + pa_idxset_free(m->idle_sources, NULL, NULL); + pa_idxset_free(m->time_sources, NULL, NULL); + + free(m->pollfds); + free(m); +} + +static void scan_dead(struct pa_mainloop *m) { + int all = 0; + assert(m); + if (m->io_sources_scan_dead) + pa_idxset_foreach(m->io_sources, foreach, &all); + if (m->fixed_sources_scan_dead) + pa_idxset_foreach(m->fixed_sources, foreach, &all); + if (m->idle_sources_scan_dead) + pa_idxset_foreach(m->idle_sources, foreach, &all); + if (m->time_sources_scan_dead) + pa_idxset_foreach(m->time_sources, foreach, &all); +} + +static void rebuild_pollfds(struct pa_mainloop *m) { + struct mainloop_source_io*s; + struct pollfd *p; + uint32_t index = PA_IDXSET_INVALID; + unsigned l; + + l = pa_idxset_ncontents(m->io_sources); + if (m->max_pollfds < l) { + m->pollfds = realloc(m->pollfds, sizeof(struct pollfd)*l); + m->max_pollfds = l; + } + + m->n_pollfds = 0; + p = m->pollfds; + for (s = pa_idxset_first(m->io_sources, &index); s; s = pa_idxset_next(m->io_sources, &index)) { + if (s->header.dead) { + s->pollfd = NULL; + continue; + } + + s->pollfd = p; + p->fd = s->fd; + p->events = ((s->events & PA_MAINLOOP_API_IO_EVENT_INPUT) ? POLLIN : 0) | ((s->events & PA_MAINLOOP_API_IO_EVENT_OUTPUT) ? POLLOUT : 0); + p->revents = 0; + + p++; + m->n_pollfds++; + } +} + +static void dispatch_pollfds(struct pa_mainloop *m) { + uint32_t index = PA_IDXSET_INVALID; + struct mainloop_source_io *s; + + for (s = pa_idxset_first(m->io_sources, &index); s; s = pa_idxset_next(m->io_sources, &index)) { + if (s->header.dead || !s->pollfd || !s->pollfd->revents) + continue; + + assert(s->pollfd->fd == s->fd && s->callback); + s->callback(&m->api, s, s->fd, + ((s->pollfd->revents & POLLHUP) ? PA_MAINLOOP_API_IO_EVENT_HUP : 0) | + ((s->pollfd->revents & POLLIN) ? PA_MAINLOOP_API_IO_EVENT_INPUT : 0) | + ((s->pollfd->revents & POLLOUT) ? PA_MAINLOOP_API_IO_EVENT_OUTPUT : 0), s->userdata); + s->pollfd->revents = 0; + } +} + +static void run_fixed_or_idle(struct pa_mainloop *m, struct pa_idxset *i) { + uint32_t index = PA_IDXSET_INVALID; + struct mainloop_source_fixed_or_idle *s; + + for (s = pa_idxset_first(i, &index); s; s = pa_idxset_next(i, &index)) { + if (s->header.dead || !s->enabled) + continue; + + assert(s->callback); + s->callback(&m->api, s, s->userdata); + } +} + +static int calc_next_timeout(struct pa_mainloop *m) { + uint32_t index = PA_IDXSET_INVALID; + struct mainloop_source_time *s; + struct timeval now; + int t = -1; + + if (pa_idxset_isempty(m->time_sources)) + return -1; + + gettimeofday(&now, NULL); + + for (s = pa_idxset_first(m->time_sources, &index); s; s = pa_idxset_next(m->time_sources, &index)) { + int tmp; + + if (s->header.dead || !s->enabled) + continue; + + if (s->timeval.tv_sec < now.tv_sec || (s->timeval.tv_sec == now.tv_sec && s->timeval.tv_usec <= now.tv_usec)) + return 0; + + tmp = (s->timeval.tv_sec - now.tv_sec)*1000; + + if (s->timeval.tv_usec > now.tv_usec) + tmp += (s->timeval.tv_usec - now.tv_usec)/1000; + else + tmp -= (now.tv_usec - s->timeval.tv_usec)/1000; + + if (tmp == 0) + return 0; + else if (t == -1 || tmp < t) + t = tmp; + } + + return t; +} + +static void dispatch_timeout(struct pa_mainloop *m) { + uint32_t index = PA_IDXSET_INVALID; + struct mainloop_source_time *s; + struct timeval now; + assert(m); + + if (pa_idxset_isempty(m->time_sources)) + return; + + gettimeofday(&now, NULL); + for (s = pa_idxset_first(m->time_sources, &index); s; s = pa_idxset_next(m->time_sources, &index)) { + + if (s->header.dead || !s->enabled) + continue; + + if (s->timeval.tv_sec < now.tv_sec || (s->timeval.tv_sec == now.tv_sec && s->timeval.tv_usec <= now.tv_usec)) { + assert(s->callback); + + s->enabled = 0; + s->callback(&m->api, s, &s->timeval, s->userdata); + } + } +} + +static int any_idle_sources(struct pa_mainloop *m) { + struct mainloop_source_fixed_or_idle *s; + uint32_t index; + assert(m); + + for (s = pa_idxset_first(m->idle_sources, &index); s; s = pa_idxset_next(m->idle_sources, &index)) + if (!s->header.dead && s->enabled) + return 1; + + return 0; +} + +int pa_mainloop_iterate(struct pa_mainloop *m, int block, int *retval) { + int r, idle; + assert(m && !m->running); + + if(m->quit) { + if (retval) + *retval = m->retval; + return 1; + } + + m->running = 1; + + scan_dead(m); + run_fixed_or_idle(m, m->fixed_sources); + + if (m->rebuild_pollfds) { + rebuild_pollfds(m); + m->rebuild_pollfds = 0; + } + + idle = any_idle_sources(m); + + do { + int t; + + if (!block || idle) + t = 0; + else + t = calc_next_timeout(m); + + r = poll(m->pollfds, m->n_pollfds, t); + } while (r < 0 && errno == EINTR); + + dispatch_timeout(m); + + if (r > 0) + dispatch_pollfds(m); + else if (r == 0 && idle) + run_fixed_or_idle(m, m->idle_sources); + else if (r < 0) + fprintf(stderr, "select(): %s\n", strerror(errno)); + + m->running = 0; + return r < 0 ? -1 : 0; +} + +int pa_mainloop_run(struct pa_mainloop *m, int *retval) { + int r; + while ((r = pa_mainloop_iterate(m, 1, retval)) == 0); + return r; +} + +void pa_mainloop_quit(struct pa_mainloop *m, int r) { + assert(m); + m->quit = r; +} + +/* IO sources */ +static void* mainloop_source_io(struct pa_mainloop_api*a, int fd, enum pa_mainloop_api_io_events events, void (*callback) (struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata) { + struct pa_mainloop *m; + struct mainloop_source_io *s; + assert(a && a->userdata && fd >= 0 && callback); + m = a->userdata; + assert(a == &m->api); + + s = malloc(sizeof(struct mainloop_source_io)); + assert(s); + s->header.mainloop = m; + s->header.dead = 0; + + s->fd = fd; + s->events = events; + s->callback = callback; + s->userdata = userdata; + s->pollfd = NULL; + + pa_idxset_put(m->io_sources, s, NULL); + m->rebuild_pollfds = 1; + return s; +} + +static void mainloop_enable_io(struct pa_mainloop_api*a, void* id, enum pa_mainloop_api_io_events events) { + struct pa_mainloop *m; + struct mainloop_source_io *s = id; + assert(a && a->userdata && s && !s->header.dead); + m = a->userdata; + assert(a == &m->api && s->header.mainloop == m); + + s->events = events; + if (s->pollfd) + s->pollfd->events = ((s->events & PA_MAINLOOP_API_IO_EVENT_INPUT) ? POLLIN : 0) | ((s->events & PA_MAINLOOP_API_IO_EVENT_OUTPUT) ? POLLOUT : 0); +} + +static void mainloop_cancel_io(struct pa_mainloop_api*a, void* id) { + struct pa_mainloop *m; + struct mainloop_source_io *s = id; + assert(a && a->userdata && s && !s->header.dead); + m = a->userdata; + assert(a == &m->api && s->header.mainloop == m); + + s->header.dead = 1; + m->io_sources_scan_dead = 1; + m->rebuild_pollfds = 1; +} + +/* Fixed sources */ +static void* mainloop_source_fixed(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, void *id, void *userdata), void *userdata) { + struct pa_mainloop *m; + struct mainloop_source_fixed_or_idle *s; + assert(a && a->userdata && callback); + m = a->userdata; + assert(a == &m->api); + + s = malloc(sizeof(struct mainloop_source_fixed_or_idle)); + assert(s); + s->header.mainloop = m; + s->header.dead = 0; + + s->enabled = 1; + s->callback = callback; + s->userdata = userdata; + + pa_idxset_put(m->fixed_sources, s, NULL); + return s; +} + +static void mainloop_enable_fixed(struct pa_mainloop_api*a, void* id, int b) { + struct pa_mainloop *m; + struct mainloop_source_fixed_or_idle *s = id; + assert(a && a->userdata && s && !s->header.dead); + m = a->userdata; + assert(a == &m->api); + + s->enabled = b; +} + +static void mainloop_cancel_fixed(struct pa_mainloop_api*a, void* id) { + struct pa_mainloop *m; + struct mainloop_source_fixed_or_idle *s = id; + assert(a && a->userdata && s && !s->header.dead); + m = a->userdata; + assert(a == &m->api); + + s->header.dead = 1; + m->fixed_sources_scan_dead = 1; +} + +/* Idle sources */ +static void* mainloop_source_idle(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, void *id, void *userdata), void *userdata) { + struct pa_mainloop *m; + struct mainloop_source_fixed_or_idle *s; + assert(a && a->userdata && callback); + m = a->userdata; + assert(a == &m->api); + + s = malloc(sizeof(struct mainloop_source_fixed_or_idle)); + assert(s); + s->header.mainloop = m; + s->header.dead = 0; + + s->enabled = 1; + s->callback = callback; + s->userdata = userdata; + + pa_idxset_put(m->idle_sources, s, NULL); + return s; +} + +static void mainloop_cancel_idle(struct pa_mainloop_api*a, void* id) { + struct pa_mainloop *m; + struct mainloop_source_fixed_or_idle *s = id; + assert(a && a->userdata && s && !s->header.dead); + m = a->userdata; + assert(a == &m->api); + + s->header.dead = 1; + m->idle_sources_scan_dead = 1; +} + +/* Time sources */ +static void* mainloop_source_time(struct pa_mainloop_api*a, const struct timeval *tv, void (*callback) (struct pa_mainloop_api*a, void *id, const struct timeval *tv, void *userdata), void *userdata) { + struct pa_mainloop *m; + struct mainloop_source_time *s; + assert(a && a->userdata && callback); + m = a->userdata; + assert(a == &m->api); + + s = malloc(sizeof(struct mainloop_source_time)); + assert(s); + s->header.mainloop = m; + s->header.dead = 0; + + s->enabled = !!tv; + if (tv) + s->timeval = *tv; + + s->callback = callback; + s->userdata = userdata; + + pa_idxset_put(m->time_sources, s, NULL); + return s; +} + +static void mainloop_enable_time(struct pa_mainloop_api*a, void *id, const struct timeval *tv) { + struct pa_mainloop *m; + struct mainloop_source_time *s = id; + assert(a && a->userdata && s && !s->header.dead); + m = a->userdata; + assert(a == &m->api); + + if (tv) { + s->enabled = 1; + s->timeval = *tv; + } else + s->enabled = 0; +} + +static void mainloop_cancel_time(struct pa_mainloop_api*a, void* id) { + struct pa_mainloop *m; + struct mainloop_source_time *s = id; + assert(a && a->userdata && s && !s->header.dead); + m = a->userdata; + assert(a == &m->api); + + s->header.dead = 1; + m->time_sources_scan_dead = 1; + +} + +static void mainloop_quit(struct pa_mainloop_api*a, int retval) { + struct pa_mainloop *m; + assert(a && a->userdata); + m = a->userdata; + assert(a == &m->api); + + m->quit = 1; + m->retval = retval; +} + +static void setup_api(struct pa_mainloop *m) { + assert(m); + + m->api.userdata = m; + m->api.source_io = mainloop_source_io; + m->api.enable_io = mainloop_enable_io; + m->api.cancel_io = mainloop_cancel_io; + + m->api.source_fixed = mainloop_source_fixed; + m->api.enable_fixed = mainloop_enable_fixed; + m->api.cancel_fixed = mainloop_cancel_fixed; + + m->api.source_idle = mainloop_source_idle; + m->api.enable_idle = mainloop_enable_fixed; /* (!) */ + m->api.cancel_idle = mainloop_cancel_idle; + + m->api.source_time = mainloop_source_time; + m->api.enable_time = mainloop_enable_time; + m->api.cancel_time = mainloop_cancel_time; + + m->api.quit = mainloop_quit; +} + +struct pa_mainloop_api* pa_mainloop_get_api(struct pa_mainloop*m) { + assert(m); + return &m->api; +} + diff --git a/polyp/mainloop.h b/polyp/mainloop.h new file mode 100644 index 00000000..58448c3e --- /dev/null +++ b/polyp/mainloop.h @@ -0,0 +1,37 @@ +#ifndef foomainloophfoo +#define foomainloophfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "mainloop-api.h" + +struct pa_mainloop; + +struct pa_mainloop *pa_mainloop_new(void); +void pa_mainloop_free(struct pa_mainloop* m); + +int pa_mainloop_iterate(struct pa_mainloop *m, int block, int *retval); +int pa_mainloop_run(struct pa_mainloop *m, int *retval); + +struct pa_mainloop_api* pa_mainloop_get_api(struct pa_mainloop*m); + +#endif diff --git a/polyp/memblock.c b/polyp/memblock.c new file mode 100644 index 00000000..8f24ff22 --- /dev/null +++ b/polyp/memblock.c @@ -0,0 +1,113 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "memblock.h" + +static unsigned memblock_count = 0, memblock_total = 0; + +struct pa_memblock *pa_memblock_new(size_t length) { + struct pa_memblock *b = malloc(sizeof(struct pa_memblock)+length); + b->type = PA_MEMBLOCK_APPENDED; + b->ref = 1; + b->length = length; + b->data = b+1; + memblock_count++; + memblock_total += length; + return b; +} + +struct pa_memblock *pa_memblock_new_fixed(void *d, size_t length) { + struct pa_memblock *b = malloc(sizeof(struct pa_memblock)); + b->type = PA_MEMBLOCK_FIXED; + b->ref = 1; + b->length = length; + b->data = d; + memblock_count++; + memblock_total += length; + return b; +} + +struct pa_memblock *pa_memblock_new_dynamic(void *d, size_t length) { + struct pa_memblock *b = malloc(sizeof(struct pa_memblock)); + b->type = PA_MEMBLOCK_DYNAMIC; + b->ref = 1; + b->length = length; + b->data = d; + memblock_count++; + memblock_total += length; + return b; +} + +struct pa_memblock* pa_memblock_ref(struct pa_memblock*b) { + assert(b && b->ref >= 1); + b->ref++; + return b; +} + +void pa_memblock_unref(struct pa_memblock*b) { + assert(b && b->ref >= 1); + b->ref--; + + if (b->ref == 0) { + if (b->type == PA_MEMBLOCK_DYNAMIC) + free(b->data); + + memblock_count--; + memblock_total -= b->length; + + free(b); + } +} + +void pa_memblock_unref_fixed(struct pa_memblock *b) { + void *d; + + assert(b && b->ref >= 1); + + if (b->ref == 1) { + pa_memblock_unref(b); + return; + } else { + d = malloc(b->length); + assert(d); + memcpy(d, b->data, b->length); + b->data = d; + b->type = PA_MEMBLOCK_DYNAMIC; + b->ref--; + } +} + +unsigned pa_memblock_get_count(void) { + return memblock_count; +} + +unsigned pa_memblock_get_total(void) { + return memblock_total; +} diff --git a/polyp/memblock.h b/polyp/memblock.h new file mode 100644 index 00000000..4bb02977 --- /dev/null +++ b/polyp/memblock.h @@ -0,0 +1,49 @@ +#ifndef foomemblockhfoo +#define foomemblockhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +enum pa_memblock_type { PA_MEMBLOCK_FIXED, PA_MEMBLOCK_APPENDED, PA_MEMBLOCK_DYNAMIC }; + +struct pa_memblock { + enum pa_memblock_type type; + unsigned ref; + size_t length; + void *data; +}; + +struct pa_memblock *pa_memblock_new(size_t length); +struct pa_memblock *pa_memblock_new_fixed(void *data, size_t length); +struct pa_memblock *pa_memblock_new_dynamic(void *data, size_t length); + +void pa_memblock_unref(struct pa_memblock*b); +struct pa_memblock* pa_memblock_ref(struct pa_memblock*b); + +void pa_memblock_unref_fixed(struct pa_memblock*b); + +unsigned pa_memblock_get_count(void); +unsigned pa_memblock_get_total(void); + +#endif diff --git a/polyp/memblockq.c b/polyp/memblockq.c new file mode 100644 index 00000000..eff923b9 --- /dev/null +++ b/polyp/memblockq.c @@ -0,0 +1,326 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "memblockq.h" + +struct memblock_list { + struct memblock_list *next; + struct pa_memchunk chunk; + struct timeval stamp; +}; + +struct pa_memblockq { + struct memblock_list *blocks, *blocks_tail; + unsigned n_blocks; + size_t current_length, maxlength, tlength, base, prebuf, minreq; + int measure_delay; + uint32_t delay; + struct pa_mcalign *mcalign; +}; + +struct pa_memblockq* pa_memblockq_new(size_t maxlength, size_t tlength, size_t base, size_t prebuf, size_t minreq) { + struct pa_memblockq* bq; + assert(maxlength && base && maxlength); + + bq = malloc(sizeof(struct pa_memblockq)); + assert(bq); + bq->blocks = bq->blocks_tail = 0; + bq->n_blocks = 0; + + bq->current_length = 0; + + fprintf(stderr, "memblockq requested: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", maxlength, tlength, base, prebuf, minreq); + + bq->base = base; + + bq->maxlength = ((maxlength+base-1)/base)*base; + assert(bq->maxlength >= base); + + bq->tlength = ((tlength+base-1)/base)*base; + if (bq->tlength == 0 || bq->tlength >= bq->maxlength) + bq->tlength = bq->maxlength; + + bq->prebuf = (prebuf == (size_t) -1) ? bq->maxlength/2 : prebuf; + bq->prebuf = (bq->prebuf/base)*base; + if (bq->prebuf > bq->maxlength) + bq->prebuf = bq->maxlength; + + bq->minreq = (minreq/base)*base; + if (bq->minreq == 0) + bq->minreq = 1; + + fprintf(stderr, "memblockq sanitized: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", bq->maxlength, bq->tlength, bq->base, bq->prebuf, bq->minreq); + + bq->measure_delay = 0; + bq->delay = 0; + + bq->mcalign = NULL; + + return bq; +} + +void pa_memblockq_free(struct pa_memblockq* bq) { + struct memblock_list *l; + assert(bq); + + if (bq->mcalign) + pa_mcalign_free(bq->mcalign); + + while ((l = bq->blocks)) { + bq->blocks = l->next; + pa_memblock_unref(l->chunk.memblock); + free(l); + } + + free(bq); +} + +void pa_memblockq_push(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta) { + struct memblock_list *q; + assert(bq && chunk && chunk->memblock && chunk->length && (chunk->length % bq->base) == 0); + + if (bq->blocks_tail && bq->blocks_tail->chunk.memblock == chunk->memblock) { + /* Try to merge memory chunks */ + + if (bq->blocks_tail->chunk.index+bq->blocks_tail->chunk.length == chunk->index) { + bq->blocks_tail->chunk.length += chunk->length; + bq->current_length += chunk->length; + + /* fprintf(stderr, __FILE__": merge succeeded: %u\n", chunk->length);*/ + return; + } + } + + q = malloc(sizeof(struct memblock_list)); + assert(q); + + if (bq->measure_delay) + gettimeofday(&q->stamp, NULL); + else + timerclear(&q->stamp); + + q->chunk = *chunk; + pa_memblock_ref(q->chunk.memblock); + assert(q->chunk.index+q->chunk.length <= q->chunk.memblock->length); + q->next = NULL; + + if (bq->blocks_tail) + bq->blocks_tail->next = q; + else + bq->blocks = q; + + bq->blocks_tail = q; + + bq->n_blocks++; + bq->current_length += chunk->length; + + pa_memblockq_shorten(bq, bq->maxlength); +} + +int pa_memblockq_peek(struct pa_memblockq* bq, struct pa_memchunk *chunk) { + assert(bq && chunk); + + if (!bq->blocks || bq->current_length < bq->prebuf) + return -1; + + bq->prebuf = 0; + + *chunk = bq->blocks->chunk; + pa_memblock_ref(chunk->memblock); + +/* if (chunk->memblock->ref != 2) */ +/* fprintf(stderr, "block %p with ref %u peeked.\n", chunk->memblock, chunk->memblock->ref); */ + + return 0; +} + +/* +int memblockq_pop(struct memblockq* bq, struct pa_memchunk *chunk) { + struct memblock_list *q; + + assert(bq && chunk); + + if (!bq->blocks || bq->current_length < bq->prebuf) + return -1; + + bq->prebuf = 0; + + q = bq->blocks; + bq->blocks = bq->blocks->next; + + *chunk = q->chunk; + + bq->n_blocks--; + bq->current_length -= chunk->length; + + free(q); + return 0; +} +*/ + +static uint32_t age(struct timeval *tv) { + assert(tv); + struct timeval now; + uint32_t r; + + if (tv->tv_sec == 0) + return 0; + + gettimeofday(&now, NULL); + + r = (now.tv_sec-tv->tv_sec) * 1000000; + + if (now.tv_usec >= tv->tv_usec) + r += now.tv_usec - tv->tv_usec; + else + r -= tv->tv_usec - now.tv_usec; + + return r; +} + +void pa_memblockq_drop(struct pa_memblockq *bq, size_t length) { + assert(bq && length && (length % bq->base) == 0); + + while (length > 0) { + size_t l = length; + assert(bq->blocks && bq->current_length >= length); + + if (l > bq->blocks->chunk.length) + l = bq->blocks->chunk.length; + + if (bq->measure_delay) + bq->delay = age(&bq->blocks->stamp); + + bq->blocks->chunk.index += l; + bq->blocks->chunk.length -= l; + bq->current_length -= l; + + if (bq->blocks->chunk.length == 0) { + struct memblock_list *q; + + q = bq->blocks; + bq->blocks = bq->blocks->next; + if (bq->blocks == NULL) + bq->blocks_tail = NULL; + pa_memblock_unref(q->chunk.memblock); + free(q); + + bq->n_blocks--; + } + + length -= l; + } +} + +void pa_memblockq_shorten(struct pa_memblockq *bq, size_t length) { + size_t l; + assert(bq); + + if (bq->current_length <= length) + return; + + fprintf(stderr, "Warning! pa_memblockq_shorten()\n"); + + l = bq->current_length - length; + l /= bq->base; + l *= bq->base; + + pa_memblockq_drop(bq, l); +} + + +void pa_memblockq_empty(struct pa_memblockq *bq) { + assert(bq); + pa_memblockq_shorten(bq, 0); +} + +int pa_memblockq_is_readable(struct pa_memblockq *bq) { + assert(bq); + + return bq->current_length && (bq->current_length >= bq->prebuf); +} + +int pa_memblockq_is_writable(struct pa_memblockq *bq, size_t length) { + assert(bq); + + return bq->current_length + length <= bq->tlength; +} + +uint32_t pa_memblockq_get_delay(struct pa_memblockq *bq) { + assert(bq); + return bq->delay; +} + +uint32_t pa_memblockq_get_length(struct pa_memblockq *bq) { + assert(bq); + return bq->current_length; +} + +uint32_t pa_memblockq_missing(struct pa_memblockq *bq) { + size_t l; + assert(bq); + + if (bq->current_length >= bq->tlength) + return 0; + + l = bq->tlength - bq->current_length; + assert(l); + + return (l >= bq->minreq) ? l : 0; +} + +void pa_memblockq_push_align(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta) { + struct pa_memchunk rchunk; + assert(bq && chunk && bq->base); + + if (bq->base == 1) { + pa_memblockq_push(bq, chunk, delta); + return; + } + + if (!bq->mcalign) { + bq->mcalign = pa_mcalign_new(bq->base); + assert(bq->mcalign); + } + + pa_mcalign_push(bq->mcalign, chunk); + + while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) { + pa_memblockq_push(bq, &rchunk, delta); + pa_memblock_unref(rchunk.memblock); + delta = 0; + } +} + +uint32_t pa_memblockq_get_minreq(struct pa_memblockq *bq) { + assert(bq); + return bq->minreq; +} diff --git a/polyp/memblockq.h b/polyp/memblockq.h new file mode 100644 index 00000000..e6ad01db --- /dev/null +++ b/polyp/memblockq.h @@ -0,0 +1,82 @@ +#ifndef foomemblockqhfoo +#define foomemblockqhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "memblock.h" +#include "memchunk.h" + +struct pa_memblockq; + +/* Parameters: + - maxlength: maximum length of queue. If more data is pushed into the queue, data from the front is dropped + - length: the target length of the queue. + - base: a base value for all metrics. Only multiples of this value are popped from the queue + - prebuf: before passing the first byte out, make sure that enough bytes are in the queue + - minreq: pa_memblockq_missing() will only return values greater than this value +*/ +struct pa_memblockq* pa_memblockq_new(size_t maxlength, + size_t tlength, + size_t base, + size_t prebuf, + size_t minreq); +void pa_memblockq_free(struct pa_memblockq*bq); + +/* Push a new memory chunk into the queue. Optionally specify a value for future cancellation. This is currently not implemented, however! */ +void pa_memblockq_push(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta); + +/* Same as pa_memblockq_push(), however chunks are filtered through a mcalign object, and thus aligned to multiples of base */ +void pa_memblockq_push_align(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta); + +/* Return a copy of the next memory chunk in the queue. It is not removed from the queue */ +int pa_memblockq_peek(struct pa_memblockq* bq, struct pa_memchunk *chunk); + +/* Drop the specified bytes from the queue */ +void pa_memblockq_drop(struct pa_memblockq *bq, size_t length); + +/* Shorten the pa_memblockq to the specified length by dropping data at the end of the queue */ +void pa_memblockq_shorten(struct pa_memblockq *bq, size_t length); + +/* Empty the pa_memblockq */ +void pa_memblockq_empty(struct pa_memblockq *bq); + +/* Test if the pa_memblockq is currently readable, that is, more data than base */ +int pa_memblockq_is_readable(struct pa_memblockq *bq); + +/* Test if the pa_memblockq is currently writable for the specified amount of bytes */ +int pa_memblockq_is_writable(struct pa_memblockq *bq, size_t length); + +/* The time memory chunks stay in the queue until they are removed completely in usecs */ +uint32_t pa_memblockq_get_delay(struct pa_memblockq *bq); + +/* Return the length of the queue in bytes */ +uint32_t pa_memblockq_get_length(struct pa_memblockq *bq); + +/* Return how many bytes are missing in queue to the specified fill amount */ +uint32_t pa_memblockq_missing(struct pa_memblockq *bq); + + +uint32_t pa_memblockq_get_minreq(struct pa_memblockq *bq); + +#endif diff --git a/polyp/memchunk.c b/polyp/memchunk.c new file mode 100644 index 00000000..d27ca61a --- /dev/null +++ b/polyp/memchunk.c @@ -0,0 +1,149 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "memchunk.h" + +void pa_memchunk_make_writable(struct pa_memchunk *c) { + struct pa_memblock *n; + assert(c && c->memblock && c->memblock->ref >= 1); + + if (c->memblock->ref == 1) + return; + + n = pa_memblock_new(c->length); + assert(n); + memcpy(n->data, c->memblock->data+c->index, c->length); + pa_memblock_unref(c->memblock); + c->memblock = n; + c->index = 0; +} + + +struct pa_mcalign { + size_t base; + struct pa_memchunk chunk; + uint8_t *buffer; + size_t buffer_fill; +}; + +struct pa_mcalign *pa_mcalign_new(size_t base) { + struct pa_mcalign *m; + assert(base); + + m = malloc(sizeof(struct pa_mcalign)); + assert(m); + m->base = base; + m->chunk.memblock = NULL; + m->chunk.length = m->chunk.index = 0; + m->buffer = NULL; + m->buffer_fill = 0; + return m; +} + +void pa_mcalign_free(struct pa_mcalign *m) { + assert(m); + + free(m->buffer); + + if (m->chunk.memblock) + pa_memblock_unref(m->chunk.memblock); + + free(m); +} + +void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c) { + assert(m && c && !m->chunk.memblock && c->memblock && c->length); + + m->chunk = *c; + pa_memblock_ref(m->chunk.memblock); +} + +int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c) { + assert(m && c && m->base > m->buffer_fill); + int ret; + + if (!m->chunk.memblock) + return -1; + + if (m->buffer_fill) { + size_t l = m->base - m->buffer_fill; + if (l > m->chunk.length) + l = m->chunk.length; + assert(m->buffer && l); + + memcpy(m->buffer + m->buffer_fill, m->chunk.memblock->data + m->chunk.index, l); + m->buffer_fill += l; + m->chunk.index += l; + m->chunk.length -= l; + + if (m->chunk.length == 0) { + m->chunk.length = m->chunk.index = 0; + pa_memblock_unref(m->chunk.memblock); + m->chunk.memblock = NULL; + } + + assert(m->buffer_fill <= m->base); + if (m->buffer_fill == m->base) { + c->memblock = pa_memblock_new_dynamic(m->buffer, m->base); + assert(c->memblock); + c->index = 0; + c->length = m->base; + m->buffer = NULL; + m->buffer_fill = 0; + + return 0; + } + + return -1; + } + + m->buffer_fill = m->chunk.length % m->base; + + if (m->buffer_fill) { + assert(!m->buffer); + m->buffer = malloc(m->base); + assert(m->buffer); + m->chunk.length -= m->buffer_fill; + memcpy(m->buffer, m->chunk.memblock->data + m->chunk.index + m->chunk.length, m->buffer_fill); + } + + if (m->chunk.length) { + *c = m->chunk; + pa_memblock_ref(c->memblock); + ret = 0; + } else + ret = -1; + + m->chunk.length = m->chunk.index = 0; + pa_memblock_unref(m->chunk.memblock); + m->chunk.memblock = NULL; + + return ret; +} diff --git a/polyp/memchunk.h b/polyp/memchunk.h new file mode 100644 index 00000000..341c145c --- /dev/null +++ b/polyp/memchunk.h @@ -0,0 +1,41 @@ +#ifndef foomemchunkhfoo +#define foomemchunkhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "memblock.h" + +struct pa_memchunk { + struct pa_memblock *memblock; + size_t index, length; +}; + +void pa_memchunk_make_writable(struct pa_memchunk *c); + +struct pa_mcalign; + +struct pa_mcalign *pa_mcalign_new(size_t base); +void pa_mcalign_free(struct pa_mcalign *m); +void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c); +int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c); + +#endif diff --git a/polyp/modargs.c b/polyp/modargs.c new file mode 100644 index 00000000..280a546d --- /dev/null +++ b/polyp/modargs.c @@ -0,0 +1,288 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#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" + +struct pa_modargs; + +struct entry { + char *key, *value; +}; + +static int add_key_value(struct pa_hashmap *map, char *key, char *value, const char* const* valid_keys) { + struct entry *e; + assert(map && key && value); + + if (valid_keys) { + const char*const* v; + for (v = valid_keys; *v; v++) + if (strcmp(*v, key) == 0) + break; + + if (!*v) { + free(key); + free(value); + return -1; + } + } + + e = malloc(sizeof(struct entry)); + assert(e); + e->key = key; + e->value = value; + pa_hashmap_put(map, key, e); + return 0; +} + +struct pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) { + struct pa_hashmap *map = NULL; + + map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + assert(map); + + if (args) { + enum { WHITESPACE, KEY, VALUE_START, VALUE_SIMPLE, VALUE_DOUBLE_QUOTES, VALUE_TICKS } state; + const char *p, *key, *value; + size_t key_len, value_len; + + key = value = NULL; + state = WHITESPACE; + for (p = args; *p; p++) { + switch (state) { + case WHITESPACE: + if (*p == '=') + goto fail; + else if (!isspace(*p)) { + key = p; + state = KEY; + key_len = 1; + } + break; + case KEY: + if (*p == '=') + state = VALUE_START; + else + key_len++; + break; + case VALUE_START: + if (*p == '\'') { + state = VALUE_TICKS; + value = p+1; + value_len = 0; + } else if (*p == '"') { + state = VALUE_DOUBLE_QUOTES; + value = p+1; + value_len = 0; + } else if (isspace(*p)) { + if (add_key_value(map, strndup(key, key_len), strdup(""), valid_keys) < 0) + goto fail; + state = WHITESPACE; + } else { + state = VALUE_SIMPLE; + value = p; + value_len = 1; + } + break; + case VALUE_SIMPLE: + if (isspace(*p)) { + if (add_key_value(map, strndup(key, key_len), strndup(value, value_len), valid_keys) < 0) + goto fail; + state = WHITESPACE; + } else + value_len++; + break; + case VALUE_DOUBLE_QUOTES: + if (*p == '"') { + if (add_key_value(map, strndup(key, key_len), strndup(value, value_len), valid_keys) < 0) + goto fail; + state = WHITESPACE; + } else + value_len++; + break; + case VALUE_TICKS: + if (*p == '\'') { + if (add_key_value(map, strndup(key, key_len), strndup(value, value_len), valid_keys) < 0) + goto fail; + state = WHITESPACE; + } else + value_len++; + break; + } + } + + if (state == VALUE_START) { + if (add_key_value(map, strndup(key, key_len), strdup(""), valid_keys) < 0) + goto fail; + } else if (state == VALUE_SIMPLE) { + if (add_key_value(map, strndup(key, key_len), strdup(value), valid_keys) < 0) + goto fail; + } else if (state != WHITESPACE) + goto fail; + } + + return (struct pa_modargs*) map; + +fail: + + if (map) + pa_modargs_free((struct pa_modargs*) map); + + return NULL; +} + + +static void free_func(void *p, void*userdata) { + struct entry *e = p; + assert(e); + free(e->key); + free(e->value); + free(e); +} + +void pa_modargs_free(struct pa_modargs*ma) { + struct pa_hashmap *map = (struct pa_hashmap*) ma; + pa_hashmap_free(map, free_func, NULL); +} + +const char *pa_modargs_get_value(struct pa_modargs *ma, const char *key, const char *def) { + struct pa_hashmap *map = (struct pa_hashmap*) ma; + struct entry*e; + + if (!(e = pa_hashmap_get(map, key))) + return def; + + return e->value; +} + +int pa_modargs_get_value_u32(struct pa_modargs *ma, const char *key, uint32_t *value) { + const char *v; + char *e; + unsigned long l; + assert(ma && key && value); + + if (!(v = pa_modargs_get_value(ma, key, NULL))) + return 0; + + if (!*v) + return -1; + + l = strtoul(v, &e, 0); + if (*e) + return -1; + + *value = (uint32_t) l; + return 0; +} + +int pa_modargs_get_sample_spec(struct pa_modargs *ma, struct pa_sample_spec *rss) { + const char *format; + uint32_t channels; + struct pa_sample_spec ss; + assert(ma && rss); + + ss = *rss; + if ((pa_modargs_get_value_u32(ma, "rate", &ss.rate)) < 0) + return -1; + + channels = ss.channels; + if ((pa_modargs_get_value_u32(ma, "channels", &channels)) < 0) + return -1; + ss.channels = (uint8_t) channels; + + if ((format = pa_modargs_get_value(ma, "format", NULL))) { + if (strcmp(format, "s16le") == 0) + ss.format = PA_SAMPLE_S16LE; + else if (strcmp(format, "s16be") == 0) + ss.format = PA_SAMPLE_S16BE; + else if (strcmp(format, "s16ne") == 0 || strcmp(format, "s16") == 0 || strcmp(format, "16") == 0) + ss.format = PA_SAMPLE_S16NE; + else if (strcmp(format, "u8") == 0 || strcmp(format, "8") == 0) + ss.format = PA_SAMPLE_U8; + else if (strcmp(format, "float32") == 0 || strcmp(format, "float32ne") == 0) + ss.format = PA_SAMPLE_FLOAT32; + else if (strcmp(format, "float32le") == 0) + ss.format = PA_SAMPLE_FLOAT32LE; + else if (strcmp(format, "float32be") == 0) + ss.format = PA_SAMPLE_FLOAT32BE; + else if (strcmp(format, "ulaw") == 0) + ss.format = PA_SAMPLE_ULAW; + else if (strcmp(format, "alaw") == 0) + ss.format = PA_SAMPLE_ALAW; + else + return -1; + } + + if (!pa_sample_spec_valid(&ss)) + return -1; + + *rss = ss; + + return 0; +} + +int pa_modargs_get_source_index(struct pa_modargs *ma, struct pa_core *c, uint32_t *index) { + const char *t; + assert(ma && index); + + if (!(t = pa_modargs_get_value(ma, "source", NULL))) + *index = PA_IDXSET_INVALID; + else { + struct pa_source *source; + if (!(source = pa_namereg_get(c, t, PA_NAMEREG_SOURCE))) + return -1; + + *index = source->index; + } + + return 0; +} + +int pa_modargs_get_sink_index(struct pa_modargs *ma, struct pa_core *c, uint32_t *index) { + const char *t; + assert(ma && index); + + if (!(t = pa_modargs_get_value(ma, "sink", NULL))) + *index = PA_IDXSET_INVALID; + else { + struct pa_sink *sink; + if (!(sink = pa_namereg_get(c, t, PA_NAMEREG_SINK))) + return -1; + + *index = sink->index; + } + + return 0; +} diff --git a/polyp/modargs.h b/polyp/modargs.h new file mode 100644 index 00000000..301dc297 --- /dev/null +++ b/polyp/modargs.h @@ -0,0 +1,42 @@ +#ifndef foomodargshfoo +#define foomodargshfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include "sample.h" +#include "core.h" + +struct pa_modargs; + +struct pa_modargs *pa_modargs_new(const char *args, const char* const* keys); +void pa_modargs_free(struct pa_modargs*ma); + +const char *pa_modargs_get_value(struct pa_modargs *ma, const char *key, const char *def); +int pa_modargs_get_value_u32(struct pa_modargs *ma, const char *key, uint32_t *value); + +int pa_modargs_get_sample_spec(struct pa_modargs *ma, struct pa_sample_spec *ss); + +int pa_modargs_get_source_index(struct pa_modargs *ma, struct pa_core *c, uint32_t *index); +int pa_modargs_get_sink_index(struct pa_modargs *ma, struct pa_core *c, uint32_t *index); + +#endif diff --git a/polyp/module-alsa-sink.c b/polyp/module-alsa-sink.c new file mode 100644 index 00000000..8a3388af --- /dev/null +++ b/polyp/module-alsa-sink.c @@ -0,0 +1,259 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#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" + +struct userdata { + snd_pcm_t *pcm_handle; + struct pa_sink *sink; + void **io_sources; + unsigned n_io_sources; + + size_t frame_size, fragment_size; + struct pa_memchunk memchunk, silence; +}; + +static const char* const valid_modargs[] = { + "device", + "sink_name", + "format", + "channels", + "rate", + "fragments", + "fragment_size", + NULL +}; + +#define DEFAULT_SINK_NAME "alsa_output" +#define DEFAULT_DEVICE "plughw:0,0" + +static void xrun_recovery(struct userdata *u) { + assert(u); + + fprintf(stderr, "*** ALSA-XRUN (playback) ***\n"); + + if (snd_pcm_prepare(u->pcm_handle) < 0) + fprintf(stderr, "snd_pcm_prepare() failed\n"); +} + +static void do_write(struct userdata *u) { + assert(u); + + for (;;) { + struct pa_memchunk *memchunk = NULL; + snd_pcm_sframes_t frames; + + if (u->memchunk.memblock) + memchunk = &u->memchunk; + else { + if (pa_sink_render(u->sink, u->fragment_size, &u->memchunk) < 0) + memchunk = &u->silence; + else + memchunk = &u->memchunk; + } + + assert(memchunk->memblock && memchunk->memblock->data && memchunk->length && memchunk->memblock->length && (memchunk->length % u->frame_size) == 0); + + if ((frames = snd_pcm_writei(u->pcm_handle, memchunk->memblock->data + memchunk->index, memchunk->length / u->frame_size)) < 0) { + if (frames == -EAGAIN) + return; + + if (frames == -EPIPE) { + xrun_recovery(u); + continue; + } + + fprintf(stderr, "snd_pcm_writei() failed\n"); + return; + } + + if (memchunk == &u->memchunk) { + size_t l = frames * u->frame_size; + memchunk->index += l; + memchunk->length -= l; + + if (memchunk->length == 0) { + pa_memblock_unref(memchunk->memblock); + memchunk->memblock = NULL; + memchunk->index = memchunk->length = 0; + } + } + + break; + } +} + +static void io_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { + struct userdata *u = userdata; + assert(u && a && id); + + if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN) + xrun_recovery(u); + + do_write(u); +} + +static uint32_t sink_get_latency_cb(struct pa_sink *s) { + struct userdata *u = s->userdata; + snd_pcm_sframes_t frames; + assert(s && u && u->sink); + + if (snd_pcm_delay(u->pcm_handle, &frames) < 0) { + fprintf(stderr, __FILE__": failed to get delay\n"); + s->get_latency = NULL; + return 0; + } + + if (frames < 0) + frames = 0; + + return pa_samples_usec(frames * u->frame_size, &s->sample_spec); +} + +int pa_module_init(struct pa_core *c, struct pa_module*m) { + struct pa_modargs *ma = NULL; + int ret = -1; + struct userdata *u = NULL; + const char *dev; + struct pa_sample_spec ss; + unsigned periods, fragsize; + snd_pcm_uframes_t buffer_size; + size_t frame_size; + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + fprintf(stderr, __FILE__": failed to parse module arguments\n"); + goto fail; + } + + ss = c->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &ss) < 0) { + fprintf(stderr, __FILE__": failed to parse sample specification\n"); + goto fail; + } + frame_size = pa_sample_size(&ss); + + periods = 12; + fragsize = 1024; + if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) { + fprintf(stderr, __FILE__": failed to parse buffer metrics\n"); + goto fail; + } + buffer_size = fragsize/frame_size*periods; + + u = malloc(sizeof(struct userdata)); + assert(u); + memset(u, 0, sizeof(struct userdata)); + m->userdata = u; + + if (snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) { + fprintf(stderr, __FILE__": Error opening PCM device %s\n", dev); + goto fail; + } + + if (pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &buffer_size) < 0) { + fprintf(stderr, __FILE__": Failed to set hardware parameters\n"); + goto fail; + } + + u->sink = pa_sink_new(c, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss); + assert(u->sink); + + u->sink->get_latency = sink_get_latency_cb; + u->sink->userdata = u; + pa_sink_set_owner(u->sink, m); + u->sink->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev); + + if (pa_create_io_sources(u->pcm_handle, c->mainloop, &u->io_sources, &u->n_io_sources, io_callback, u) < 0) { + fprintf(stderr, __FILE__": failed to obtain file descriptors\n"); + goto fail; + } + + u->frame_size = frame_size; + u->fragment_size = buffer_size*u->frame_size/periods; + + fprintf(stderr, __FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size); + + u->silence.memblock = pa_memblock_new(u->silence.length = u->fragment_size); + assert(u->silence.memblock); + pa_silence_memblock(u->silence.memblock, &ss); + u->silence.index = 0; + + u->memchunk.memblock = NULL; + u->memchunk.index = u->memchunk.length = 0; + + ret = 0; + +finish: + if (ma) + pa_modargs_free(ma); + + return ret; + +fail: + + if (u) + pa_module_done(c, m); + + goto finish; +} + +void pa_module_done(struct pa_core *c, struct pa_module*m) { + struct userdata *u; + assert(c && m); + + if ((u = m->userdata)) { + if (u->sink) + pa_sink_free(u->sink); + + if (u->io_sources) + pa_free_io_sources(c->mainloop, u->io_sources, u->n_io_sources); + + if (u->pcm_handle) { + snd_pcm_drop(u->pcm_handle); + snd_pcm_close(u->pcm_handle); + } + + if (u->memchunk.memblock) + pa_memblock_unref(u->memchunk.memblock); + if (u->silence.memblock) + pa_memblock_unref(u->silence.memblock); + + free(u); + } +} + diff --git a/polyp/module-alsa-source.c b/polyp/module-alsa-source.c new file mode 100644 index 00000000..287a0350 --- /dev/null +++ b/polyp/module-alsa-source.c @@ -0,0 +1,237 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#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" + +struct userdata { + snd_pcm_t *pcm_handle; + struct pa_source *source; + void **io_sources; + unsigned n_io_sources; + + size_t frame_size, fragment_size; + struct pa_memchunk memchunk; +}; + +static const char* const valid_modargs[] = { + "device", + "source_name", + "format", + "channels", + "rate", + "fragments", + "fragment_size", + NULL +}; + +#define DEFAULT_SOURCE_NAME "alsa_input" +#define DEFAULT_DEVICE "hw:0,0" + +static void xrun_recovery(struct userdata *u) { + assert(u); + + fprintf(stderr, "*** ALSA-XRUN (capture) ***\n"); + + if (snd_pcm_prepare(u->pcm_handle) < 0) + fprintf(stderr, "snd_pcm_prepare() failed\n"); +} + +static void do_read(struct userdata *u) { + assert(u); + + for (;;) { + struct pa_memchunk post_memchunk; + snd_pcm_sframes_t frames; + size_t l; + + if (!u->memchunk.memblock) { + u->memchunk.memblock = pa_memblock_new(u->memchunk.length = u->fragment_size); + u->memchunk.index = 0; + } + + assert(u->memchunk.memblock && u->memchunk.memblock->data && u->memchunk.length && u->memchunk.memblock->length && (u->memchunk.length % u->frame_size) == 0); + + if ((frames = snd_pcm_readi(u->pcm_handle, u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) { + if (frames == -EAGAIN) + return; + + if (frames == -EPIPE) { + xrun_recovery(u); + continue; + } + + fprintf(stderr, "snd_pcm_readi() failed: %s\n", strerror(-frames)); + return; + } + + l = frames * u->frame_size; + + post_memchunk = u->memchunk; + post_memchunk.length = l; + + pa_source_post(u->source, &post_memchunk); + + u->memchunk.index += l; + u->memchunk.length -= l; + + if (u->memchunk.length == 0) { + pa_memblock_unref(u->memchunk.memblock); + u->memchunk.memblock = NULL; + u->memchunk.index = u->memchunk.length = 0; + } + + break; + } +} + +static void io_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { + struct userdata *u = userdata; + assert(u && a && id); + + if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN) + xrun_recovery(u); + + do_read(u); +} + +int pa_module_init(struct pa_core *c, struct pa_module*m) { + struct pa_modargs *ma = NULL; + int ret = -1; + struct userdata *u = NULL; + const char *dev; + struct pa_sample_spec ss; + unsigned periods, fragsize; + snd_pcm_uframes_t buffer_size; + size_t frame_size; + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + fprintf(stderr, __FILE__": failed to parse module arguments\n"); + goto fail; + } + + ss = c->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &ss) < 0) { + fprintf(stderr, __FILE__": failed to parse sample specification\n"); + goto fail; + } + frame_size = pa_sample_size(&ss); + + periods = 12; + fragsize = 1024; + if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) { + fprintf(stderr, __FILE__": failed to parse buffer metrics\n"); + goto fail; + } + buffer_size = fragsize/frame_size*periods; + + u = malloc(sizeof(struct userdata)); + assert(u); + memset(u, 0, sizeof(struct userdata)); + m->userdata = u; + + if (snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) < 0) { + fprintf(stderr, __FILE__": Error opening PCM device %s\n", dev); + goto fail; + } + + if (pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &buffer_size) < 0) { + fprintf(stderr, __FILE__": Failed to set hardware parameters\n"); + goto fail; + } + + u->source = pa_source_new(c, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss); + assert(u->source); + + u->source->userdata = u; + pa_source_set_owner(u->source, m); + u->source->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev); + + if (pa_create_io_sources(u->pcm_handle, c->mainloop, &u->io_sources, &u->n_io_sources, io_callback, u) < 0) { + fprintf(stderr, __FILE__": failed to obtain file descriptors\n"); + goto fail; + } + + u->frame_size = frame_size; + u->fragment_size = buffer_size*u->frame_size/periods; + + fprintf(stderr, __FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size); + + u->memchunk.memblock = NULL; + u->memchunk.index = u->memchunk.length = 0; + + snd_pcm_start(u->pcm_handle); + + ret = 0; + +finish: + if (ma) + pa_modargs_free(ma); + + return ret; + +fail: + + if (u) + pa_module_done(c, m); + + goto finish; +} + +void pa_module_done(struct pa_core *c, struct pa_module*m) { + struct userdata *u; + assert(c && m); + + if ((u = m->userdata)) { + if (u->source) + pa_source_free(u->source); + + if (u->io_sources) + pa_free_io_sources(c->mainloop, u->io_sources, u->n_io_sources); + + if (u->pcm_handle) { + snd_pcm_drop(u->pcm_handle); + snd_pcm_close(u->pcm_handle); + } + + if (u->memchunk.memblock) + pa_memblock_unref(u->memchunk.memblock); + + free(u); + } +} + diff --git a/polyp/module-cli.c b/polyp/module-cli.c new file mode 100644 index 00000000..8897c9c6 --- /dev/null +++ b/polyp/module-cli.c @@ -0,0 +1,73 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "module.h" +#include "iochannel.h" +#include "cli.h" +#include "sioman.h" + +static void eof_cb(struct pa_cli*c, void *userdata) { + struct pa_module *m = userdata; + assert(c && m); + + pa_module_unload_request(m->core, m); +} + +int pa_module_init(struct pa_core *c, struct pa_module*m) { + struct pa_iochannel *io; + assert(c && m); + + if (m->argument) { + fprintf(stderr, __FILE__": module doesn't accept arguments.\n"); + return -1; + } + + if (pa_stdio_acquire() < 0) { + fprintf(stderr, __FILE__": STDIN/STDUSE already in use.\n"); + return -1; + } + + io = pa_iochannel_new(c->mainloop, STDIN_FILENO, STDOUT_FILENO); + assert(io); + pa_iochannel_set_noclose(io, 1); + + m->userdata = pa_cli_new(c, io, m); + assert(m->userdata); + + pa_cli_set_eof_callback(m->userdata, eof_cb, m); + + return 0; +} + +void pa_module_done(struct pa_core *c, struct pa_module*m) { + assert(c && m); + + pa_cli_free(m->userdata); + pa_stdio_release(); +} diff --git a/polyp/module-oss-mmap.c b/polyp/module-oss-mmap.c new file mode 100644 index 00000000..800eaf25 --- /dev/null +++ b/polyp/module-oss-mmap.c @@ -0,0 +1,404 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#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" + +struct userdata { + struct pa_sink *sink; + struct pa_source *source; + struct pa_core *core; + struct pa_sample_spec sample_spec; + + size_t in_fragment_size, out_fragment_size, in_fragments, out_fragments, out_fill; + + int fd; + + void *in_mmap, *out_mmap; + size_t in_mmap_length, out_mmap_length; + + void *mainloop_source; + + struct pa_memblock **in_memblocks, **out_memblocks; + unsigned out_current, in_current; +}; + +static const char* const valid_modargs[] = { + "sink_name", + "source_name", + "device", + "record", + "playback", + "fragments", + "fragment_size", + "format", + "rate", + "channels", + NULL +}; + +#define DEFAULT_SINK_NAME "oss_output" +#define DEFAULT_SOURCE_NAME "oss_input" +#define DEFAULT_DEVICE "/dev/dsp" + +static void out_fill_memblocks(struct userdata *u, unsigned n) { + assert(u && u->out_memblocks); + + while (n > 0) { + struct pa_memchunk chunk; + + if (u->out_memblocks[u->out_current]) + pa_memblock_unref_fixed(u->out_memblocks[u->out_current]); + + chunk.memblock = u->out_memblocks[u->out_current] = pa_memblock_new_fixed(u->out_mmap+u->out_fragment_size*u->out_current, u->out_fragment_size); + assert(chunk.memblock); + chunk.length = chunk.memblock->length; + chunk.index = 0; + + pa_sink_render_into_full(u->sink, &chunk); + + u->out_current++; + while (u->out_current >= u->out_fragments) + u->out_current -= u->out_fragments; + + n--; + } +} + +static void do_write(struct userdata *u) { + struct count_info info; + assert(u && u->sink); + + if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) { + fprintf(stderr, "SNDCTL_DSP_GETOPTR: %s\n", strerror(errno)); + return; + } + + u->out_fill = (u->out_fragment_size * u->out_fragments) - (info.ptr % u->out_fragment_size); + + if (!info.blocks) + return; + + out_fill_memblocks(u, info.blocks); +} + +static void in_post_memblocks(struct userdata *u, unsigned n) { + assert(u && u->in_memblocks); + + while (n > 0) { + struct pa_memchunk chunk; + + if (!u->in_memblocks[u->in_current]) { + chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed(u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size); + chunk.length = chunk.memblock->length; + chunk.index = 0; + + pa_source_post(u->source, &chunk); + } + + u->in_current++; + while (u->in_current >= u->in_fragments) + u->in_current -= u->in_fragments; + + n--; + } +} + +static void in_clear_memblocks(struct userdata*u, unsigned n) { + unsigned i = u->in_current; + assert(u && u->in_memblocks); + + if (n > u->in_fragments) + n = u->in_fragments; + + while (n > 0) { + if (u->in_memblocks[i]) { + pa_memblock_unref_fixed(u->in_memblocks[i]); + u->in_memblocks[i] = NULL; + } + + i++; + while (i >= u->in_fragments) + i -= u->in_fragments; + + n--; + } +} + +static void do_read(struct userdata *u) { + struct count_info info; + assert(u && u->source); + + if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) { + fprintf(stderr, "SNDCTL_DSP_GETIPTR: %s\n", strerror(errno)); + return; + } + + if (!info.blocks) + return; + + in_post_memblocks(u, info.blocks); + in_clear_memblocks(u, u->in_fragments/2); +}; + +static void io_callback(struct pa_mainloop_api *m, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { + struct userdata *u = userdata; + + assert (u && u->core->mainloop == m && u->mainloop_source == id); + + if (events & PA_MAINLOOP_API_IO_EVENT_INPUT) + do_read(u); + if (events & PA_MAINLOOP_API_IO_EVENT_OUTPUT) + do_write(u); +} + +static uint32_t sink_get_latency_cb(struct pa_sink *s) { + struct userdata *u = s->userdata; + assert(s && u); + + do_write(u); + return pa_samples_usec(u->out_fill, &s->sample_spec); +} + +int pa_module_init(struct pa_core *c, struct pa_module*m) { + struct audio_buf_info info; + struct userdata *u = NULL; + const char *p; + int nfrags, frag_size; + int mode, caps; + int enable_bits = 0, zero = 0; + int playback = 1, record = 1; + struct pa_modargs *ma = NULL; + assert(c && m); + + m->userdata = u = malloc(sizeof(struct userdata)); + assert(u); + memset(u, 0, sizeof(struct userdata)); + u->fd = -1; + u->core = c; + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + fprintf(stderr, __FILE__": failed to parse module arguments.\n"); + goto fail; + } + + if (pa_modargs_get_value_u32(ma, "record", &record) < 0 || pa_modargs_get_value_u32(ma, "playback", &playback) < 0) { + fprintf(stderr, __FILE__": record= and playback= expect numeric arguments.\n"); + goto fail; + } + + mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0)); + if (mode == 0) { + fprintf(stderr, __FILE__": neither playback nor record enabled for device.\n"); + goto fail; + } + + nfrags = 12; + frag_size = 1024; + if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || nfrags < 2 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 || frag_size < 1) { + fprintf(stderr, __FILE__": failed to parse fragments arguments\n"); + goto fail; + } + + u->sample_spec = c->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &u->sample_spec) < 0) { + fprintf(stderr, __FILE__": failed to parse sample specification\n"); + goto fail; + } + + if ((u->fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0) + goto fail; + + if (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_REALTIME) || !(caps & DSP_CAP_TRIGGER)) { + fprintf(stderr, "OSS device not mmap capable.\n"); + goto fail; + } + + fprintf(stderr, "module-oss: device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR")); + + if (pa_oss_set_fragments(u->fd, nfrags, frag_size) < 0) + goto fail; + + if (pa_oss_auto_format(u->fd, &u->sample_spec) < 0) + goto fail; + + if (mode != O_WRONLY) { + if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) { + fprintf(stderr, "SNDCTL_DSP_GETISPACE: %s\n", strerror(errno)); + goto fail; + } + + fprintf(stderr, "module-oss-mmap: input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); + u->in_mmap_length = (u->in_fragment_size = info.fragsize) * (u->in_fragments = info.fragstotal); + + if ((u->in_mmap = mmap(NULL, u->in_mmap_length, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) { + if (mode == O_RDWR) { + fprintf(stderr, "module-oss-mmap: mmap failed for input. Changing to O_WRONLY mode.\n"); + mode = O_WRONLY; + } else { + fprintf(stderr, "modeule-oss-mmap: mmap(): %s\n", strerror(errno)); + goto fail; + } + } else { + + u->source = pa_source_new(c, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &u->sample_spec); + assert(u->source); + u->source->userdata = u; + pa_source_set_owner(u->source, m); + u->source->description = pa_sprintf_malloc("Open Sound System PCM/mmap() on '%s'", p); + + + u->in_memblocks = malloc(sizeof(struct pa_memblock *)*u->in_fragments); + memset(u->in_memblocks, 0, sizeof(struct pa_memblock *)*u->in_fragments); + + enable_bits |= PCM_ENABLE_INPUT; + } + } + + if (mode != O_RDONLY) { + if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { + fprintf(stderr, "SNDCTL_DSP_GETOSPACE: %s\n", strerror(errno)); + goto fail; + } + + fprintf(stderr, "module-oss: output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); + u->out_mmap_length = (u->out_fragment_size = info.fragsize) * (u->out_fragments = info.fragstotal); + + if ((u->out_mmap = mmap(NULL, u->out_mmap_length, PROT_WRITE, MAP_SHARED, u->fd, 0)) == MAP_FAILED) { + if (mode == O_RDWR) { + fprintf(stderr, "module-oss-mmap: mmap filed for output. Changing to O_RDONLY mode.\n"); + mode = O_RDONLY; + } else { + fprintf(stderr, "module-oss-mmap: mmap(): %s\n", strerror(errno)); + goto fail; + } + } else { + pa_silence_memory(u->out_mmap, u->out_mmap_length, &u->sample_spec); + + u->sink = pa_sink_new(c, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &u->sample_spec); + assert(u->sink); + u->sink->get_latency = sink_get_latency_cb; + u->sink->userdata = u; + pa_sink_set_owner(u->sink, m); + u->sink->description = pa_sprintf_malloc("Open Sound System PCM/mmap() on '%s'", p); + + u->out_memblocks = malloc(sizeof(struct memblock *)*u->out_fragments); + memset(u->out_memblocks, 0, sizeof(struct pa_memblock *)*u->out_fragments); + + enable_bits |= PCM_ENABLE_OUTPUT; + } + } + + zero = 0; + if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero) < 0) { + fprintf(stderr, "SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno)); + goto fail; + } + + if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) { + fprintf(stderr, "SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno)); + goto fail; + } + + assert(u->source || u->sink); + + u->mainloop_source = c->mainloop->source_io(c->mainloop, u->fd, (u->source ? PA_MAINLOOP_API_IO_EVENT_INPUT : 0) | (u->sink ? PA_MAINLOOP_API_IO_EVENT_OUTPUT : 0), io_callback, u); + assert(u->mainloop_source); + + pa_modargs_free(ma); + + return 0; + +fail: + pa_module_done(c, m); + + if (ma) + pa_modargs_free(ma); + + return -1; +} + +void pa_module_done(struct pa_core *c, struct pa_module*m) { + struct userdata *u; + assert(c && m); + + u = m->userdata; + assert(u); + + if (u->out_memblocks) { + unsigned i; + for (i = 0; i < u->out_fragments; i++) + if (u->out_memblocks[i]) + pa_memblock_unref_fixed(u->out_memblocks[i]); + free(u->out_memblocks); + } + + if (u->in_memblocks) { + unsigned i; + for (i = 0; i < u->in_fragments; i++) + if (u->in_memblocks[i]) + pa_memblock_unref_fixed(u->in_memblocks[i]); + free(u->in_memblocks); + } + + if (u->in_mmap && u->in_mmap != MAP_FAILED) + munmap(u->in_mmap, u->in_mmap_length); + + if (u->out_mmap && u->out_mmap != MAP_FAILED) + munmap(u->out_mmap, u->out_mmap_length); + + if (u->sink) + pa_sink_free(u->sink); + + if (u->source) + pa_source_free(u->source); + + if (u->mainloop_source) + u->core->mainloop->cancel_io(u->core->mainloop, u->mainloop_source); + + if (u->fd >= 0) + close(u->fd); + + free(u); +} diff --git a/polyp/module-oss.c b/polyp/module-oss.c new file mode 100644 index 00000000..d727534a --- /dev/null +++ b/polyp/module-oss.c @@ -0,0 +1,304 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#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" + +struct userdata { + struct pa_sink *sink; + struct pa_source *source; + struct pa_iochannel *io; + struct pa_core *core; + + struct pa_memchunk memchunk, silence; + + uint32_t in_fragment_size, out_fragment_size, sample_size; + + int fd; +}; + +static const char* const valid_modargs[] = { + "sink_name", + "source_name", + "device", + "record", + "playback", + "fragments", + "fragment_size", + "format", + "rate", + "channels", + NULL +}; + +#define DEFAULT_SINK_NAME "oss_output" +#define DEFAULT_SOURCE_NAME "oss_input" +#define DEFAULT_DEVICE "/dev/dsp" + +static void do_write(struct userdata *u) { + struct pa_memchunk *memchunk; + ssize_t r; + assert(u); + + if (!u->sink || !pa_iochannel_is_writable(u->io)) + return; + + if (!u->memchunk.length) { + if (pa_sink_render(u->sink, u->out_fragment_size, &u->memchunk) < 0) + memchunk = &u->silence; + else + memchunk = &u->memchunk; + } + + assert(memchunk->memblock && memchunk->length); + + if ((r = pa_iochannel_write(u->io, memchunk->memblock->data + memchunk->index, memchunk->length)) < 0) { + fprintf(stderr, "write() failed: %s\n", strerror(errno)); + return; + } + + if (memchunk == &u->silence) + assert(r % u->sample_size == 0); + else { + u->memchunk.index += r; + u->memchunk.length -= r; + + if (u->memchunk.length <= 0) { + pa_memblock_unref(u->memchunk.memblock); + u->memchunk.memblock = NULL; + } + } +} + +static void do_read(struct userdata *u) { + struct pa_memchunk memchunk; + ssize_t r; + assert(u); + + if (!u->source || !pa_iochannel_is_readable(u->io)) + return; + + memchunk.memblock = pa_memblock_new(u->in_fragment_size); + assert(memchunk.memblock); + if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) { + pa_memblock_unref(memchunk.memblock); + if (errno != EAGAIN) + fprintf(stderr, "read() failed: %s\n", strerror(errno)); + return; + } + + assert(r <= (ssize_t) memchunk.memblock->length); + memchunk.length = memchunk.memblock->length = r; + memchunk.index = 0; + + pa_source_post(u->source, &memchunk); + pa_memblock_unref(memchunk.memblock); +}; + +static void io_callback(struct pa_iochannel *io, void*userdata) { + struct userdata *u = userdata; + assert(u); + do_write(u); + do_read(u); +} + +static uint32_t sink_get_latency_cb(struct pa_sink *s) { + int arg; + struct userdata *u = s->userdata; + assert(s && u && u->sink); + + if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) { + fprintf(stderr, "module-oss: device doesn't support SNDCTL_DSP_GETODELAY.\n"); + s->get_latency = NULL; + return 0; + } + + return pa_samples_usec(arg, &s->sample_spec); +} + +int pa_module_init(struct pa_core *c, struct pa_module*m) { + struct audio_buf_info info; + struct userdata *u = NULL; + const char *p; + int fd = -1; + int nfrags, frag_size, in_frag_size, out_frag_size; + int mode; + uint32_t record = 1, playback = 1; + struct pa_sample_spec ss; + struct pa_modargs *ma = NULL; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + fprintf(stderr, __FILE__": failed to parse module arguments.\n"); + goto fail; + } + + if (pa_modargs_get_value_u32(ma, "record", &record) < 0 || pa_modargs_get_value_u32(ma, "playback", &playback) < 0) { + fprintf(stderr, __FILE__": record= and playback= expect numeric argument.\n"); + goto fail; + } + + mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0)); + if (mode == 0) { + fprintf(stderr, __FILE__": neither playback nor record enabled for device.\n"); + goto fail; + } + + nfrags = 12; + frag_size = 1024; + if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || nfrags < 2 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 || frag_size < 1) { + fprintf(stderr, __FILE__": failed to parse fragments arguments\n"); + goto fail; + } + + ss = c->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &ss) < 0) { + fprintf(stderr, __FILE__": failed to parse sample specification\n"); + goto fail; + } + + if ((fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, NULL)) < 0) + goto fail; + + fprintf(stderr, "module-oss: device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR")); + + if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0) + goto fail; + + if (pa_oss_auto_format(fd, &ss) < 0) + goto fail; + + if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) { + fprintf(stderr, "SNDCTL_DSP_GETBLKSIZE: %s\n", strerror(errno)); + goto fail; + } + assert(frag_size); + in_frag_size = out_frag_size = frag_size; + + if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) { + fprintf(stderr, "module-oss: input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); + in_frag_size = info.fragsize; + } + + if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) { + fprintf(stderr, "module-oss: output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); + out_frag_size = info.fragsize; + } + + u = malloc(sizeof(struct userdata)); + assert(u); + + u->core = c; + + if (mode != O_WRONLY) { + u->source = pa_source_new(c, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss); + assert(u->source); + u->source->userdata = u; + pa_source_set_owner(u->source, m); + u->source->description = pa_sprintf_malloc("Open Sound System PCM on '%s'", p); + } else + u->source = NULL; + + if (mode != O_RDONLY) { + u->sink = pa_sink_new(c, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss); + assert(u->sink); + u->sink->get_latency = sink_get_latency_cb; + u->sink->userdata = u; + pa_sink_set_owner(u->sink, m); + u->sink->description = pa_sprintf_malloc("Open Sound System PCM on '%s'", p); + } else + u->sink = NULL; + + assert(u->source || u->sink); + + u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0); + assert(u->io); + pa_iochannel_set_callback(u->io, io_callback, u); + u->fd = fd; + + u->memchunk.memblock = NULL; + u->memchunk.length = 0; + u->sample_size = pa_sample_size(&ss); + + u->out_fragment_size = out_frag_size; + u->in_fragment_size = in_frag_size; + u->silence.memblock = pa_memblock_new(u->silence.length = u->out_fragment_size); + assert(u->silence.memblock); + pa_silence_memblock(u->silence.memblock, &ss); + u->silence.index = 0; + + m->userdata = u; + + pa_modargs_free(ma); + + return 0; + +fail: + if (fd >= 0) + close(fd); + + if (ma) + pa_modargs_free(ma); + + return -1; +} + +void pa_module_done(struct pa_core *c, struct pa_module*m) { + struct userdata *u; + assert(c && m); + + u = m->userdata; + assert(u); + + if (u->memchunk.memblock) + pa_memblock_unref(u->memchunk.memblock); + if (u->silence.memblock) + pa_memblock_unref(u->silence.memblock); + + if (u->sink) + pa_sink_free(u->sink); + if (u->source) + pa_source_free(u->source); + pa_iochannel_free(u->io); + free(u); +} diff --git a/polyp/module-pipe-sink.c b/polyp/module-pipe-sink.c new file mode 100644 index 00000000..df34f73a --- /dev/null +++ b/polyp/module-pipe-sink.c @@ -0,0 +1,218 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#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" + +#define DEFAULT_FIFO_NAME "/tmp/musicfifo" +#define DEFAULT_SINK_NAME "fifo_output" + +struct userdata { + struct pa_core *core; + + char *filename; + + struct pa_sink *sink; + struct pa_iochannel *io; + void *mainloop_source; + + struct pa_memchunk memchunk; +}; + +static const char* const valid_modargs[] = { + "file", + "rate", + "channels", + "format", + "sink_name", + NULL +}; + +static void do_write(struct userdata *u) { + ssize_t r; + assert(u); + + u->core->mainloop->enable_fixed(u->core->mainloop, u->mainloop_source, 0); + + if (!pa_iochannel_is_writable(u->io)) + return; + + if (!u->memchunk.length) + if (pa_sink_render(u->sink, PIPE_BUF, &u->memchunk) < 0) + return; + + assert(u->memchunk.memblock && u->memchunk.length); + + if ((r = pa_iochannel_write(u->io, u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) { + fprintf(stderr, "write() failed: %s\n", strerror(errno)); + return; + } + + u->memchunk.index += r; + u->memchunk.length -= r; + + if (u->memchunk.length <= 0) { + pa_memblock_unref(u->memchunk.memblock); + u->memchunk.memblock = NULL; + } +} + +static void notify_cb(struct pa_sink*s) { + struct userdata *u = s->userdata; + assert(s && u); + + if (pa_iochannel_is_writable(u->io)) + u->core->mainloop->enable_fixed(u->core->mainloop, u->mainloop_source, 1); +} + +static void fixed_callback(struct pa_mainloop_api *m, void *id, void *userdata) { + struct userdata *u = userdata; + assert(u); + do_write(u); +} + +static void io_callback(struct pa_iochannel *io, void*userdata) { + struct userdata *u = userdata; + assert(u); + do_write(u); +} + +int pa_module_init(struct pa_core *c, struct pa_module*m) { + struct userdata *u = NULL; + struct stat st; + const char *p; + int fd = -1; + struct pa_sample_spec ss; + struct pa_modargs *ma = NULL; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + fprintf(stderr, __FILE__": failed to parse module arguments\n"); + goto fail; + } + + ss = c->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &ss) < 0) { + fprintf(stderr, __FILE__": invalid sample format specification\n"); + goto fail; + } + + mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777); + + if ((fd = open(p, O_RDWR)) < 0) { + fprintf(stderr, __FILE__": open('%s'): %s\n", p, strerror(errno)); + goto fail; + } + + if (fstat(fd, &st) < 0) { + fprintf(stderr, __FILE__": fstat('%s'): %s\n", p, strerror(errno)); + goto fail; + } + + if (!S_ISFIFO(st.st_mode)) { + fprintf(stderr, __FILE__": '%s' is not a FIFO.\n", p); + goto fail; + } + + u = malloc(sizeof(struct userdata)); + assert(u); + memset(u, 0, sizeof(struct userdata)); + + u->filename = strdup(p); + assert(u->filename); + u->core = c; + + if (!(u->sink = pa_sink_new(c, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss))) { + fprintf(stderr, __FILE__": failed to create sink.\n"); + goto fail; + } + u->sink->notify = notify_cb; + u->sink->userdata = u; + pa_sink_set_owner(u->sink, m); + u->sink->description = pa_sprintf_malloc("Unix FIFO sink '%s'", p); + assert(u->sink->description); + + u->io = pa_iochannel_new(c->mainloop, -1, fd); + assert(u->io); + pa_iochannel_set_callback(u->io, io_callback, u); + + u->memchunk.memblock = NULL; + u->memchunk.length = 0; + + u->mainloop_source = c->mainloop->source_fixed(c->mainloop, fixed_callback, u); + assert(u->mainloop_source); + c->mainloop->enable_fixed(c->mainloop, u->mainloop_source, 0); + + m->userdata = u; + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + if (fd >= 0) + close(fd); + + pa_module_done(c, m); + + return -1; +} + +void pa_module_done(struct pa_core *c, struct pa_module*m) { + struct userdata *u; + assert(c && m); + + if (!(u = m->userdata)) + return; + + if (u->memchunk.memblock) + pa_memblock_unref(u->memchunk.memblock); + + pa_sink_free(u->sink); + pa_iochannel_free(u->io); + u->core->mainloop->cancel_fixed(u->core->mainloop, u->mainloop_source); + + assert(u->filename); + unlink(u->filename); + free(u->filename); + + free(u); +} diff --git a/polyp/module-protocol-stub.c b/polyp/module-protocol-stub.c new file mode 100644 index 00000000..3ce18c31 --- /dev/null +++ b/polyp/module-protocol-stub.c @@ -0,0 +1,165 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "module.h" +#include "socket-server.h" +#include "socket-util.h" +#include "util.h" +#include "modargs.h" + +#ifdef USE_PROTOCOL_SIMPLE + #include "protocol-simple.h" + #define protocol_new pa_protocol_simple_new + #define protocol_free pa_protocol_simple_free + #define IPV4_PORT 4711 + #define UNIX_SOCKET "/tmp/polypaudio/simple" + #define MODULE_ARGUMENTS "rate", "format", "channels", "sink", "source", "playback", "record", +#else + #ifdef USE_PROTOCOL_CLI + #include "protocol-cli.h" + #define protocol_new pa_protocol_cli_new + #define protocol_free pa_protocol_cli_free + #define IPV4_PORT 4712 + #define UNIX_SOCKET "/tmp/polypaudio/cli" + #define MODULE_ARGUMENTS + #else + #ifdef USE_PROTOCOL_NATIVE + #include "protocol-native.h" + #define protocol_new pa_protocol_native_new + #define protocol_free pa_protocol_native_free + #define IPV4_PORT 4713 + #define UNIX_SOCKET "/tmp/polypaudio/native" + #define MODULE_ARGUMENTS "public", "cookie", + #else + #ifdef USE_PROTOCOL_ESOUND + #include "protocol-esound.h" + #include "esound.h" + #define protocol_new pa_protocol_esound_new + #define protocol_free pa_protocol_esound_free + #define IPV4_PORT ESD_DEFAULT_PORT + #define UNIX_SOCKET ESD_UNIX_SOCKET_NAME + #define MODULE_ARGUMENTS "sink", "source", "public", "cookie", + #else + #error "Broken build system" + #endif + #endif + #endif +#endif + +static const char* const valid_modargs[] = { + MODULE_ARGUMENTS +#ifdef USE_TCP_SOCKETS + "port", + "loopback", +#else + "socket", +#endif + NULL +}; + +static struct pa_socket_server *create_socket_server(struct pa_core *c, struct pa_modargs *ma) { + struct pa_socket_server *s; +#ifdef USE_TCP_SOCKETS + uint32_t loopback = 1, port = IPV4_PORT; + + if (pa_modargs_get_value_u32(ma, "loopback", &loopback) < 0) { + fprintf(stderr, "loopback= expects a numerical argument.\n"); + return NULL; + } + + if (pa_modargs_get_value_u32(ma, "port", &port) < 0) { + fprintf(stderr, "port= expects a numerical argument.\n"); + return NULL; + } + + if (!(s = pa_socket_server_new_ipv4(c->mainloop, loopback ? INADDR_LOOPBACK : INADDR_ANY, port))) + return NULL; +#else + int r; + const char *p; + + p = pa_modargs_get_value(ma, "socket", UNIX_SOCKET); + assert(p); + + if (pa_unix_socket_make_secure_dir(p) < 0) { + fprintf(stderr, "Failed to create secure socket directory.\n"); + return NULL; + } + + if ((r = pa_unix_socket_remove_stale(p)) < 0) { + fprintf(stderr, "Failed to remove stale UNIX socket '%s': %s\n", p, strerror(errno)); + return NULL; + } + + if (r) + fprintf(stderr, "Removed stale UNIX socket '%s'.", p); + + if (!(s = pa_socket_server_new_unix(c->mainloop, p))) + return NULL; + +#endif + return s; +} + +int pa_module_init(struct pa_core *c, struct pa_module*m) { + struct pa_socket_server *s; + struct pa_modargs *ma = NULL; + int ret = -1; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + fprintf(stderr, "Failed to parse module arguments\n"); + goto finish; + } + + if (!(s = create_socket_server(c, ma))) + goto finish; + + if (!(m->userdata = protocol_new(c, s, m, ma))) { + pa_socket_server_free(s); + goto finish; + } + + ret = 0; + +finish: + if (ma) + pa_modargs_free(ma); + + return ret; +} + +void pa_module_done(struct pa_core *c, struct pa_module*m) { + assert(c && m); + + protocol_free(m->userdata); +} diff --git a/polyp/module.c b/polyp/module.c new file mode 100644 index 00000000..5c6f0fb6 --- /dev/null +++ b/polyp/module.c @@ -0,0 +1,161 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "module.h" + +struct pa_module* pa_module_load(struct pa_core *c, const char *name, const char *argument) { + struct pa_module *m = NULL; + int r; + + assert(c && name); + + m = malloc(sizeof(struct pa_module)); + assert(m); + + m->name = strdup(name); + m->argument = argument ? strdup(argument) : NULL; + + if (!(m->dl = lt_dlopenext(name))) + goto fail; + + if (!(m->init = lt_dlsym(m->dl, "pa_module_init"))) + goto fail; + + if (!(m->done = lt_dlsym(m->dl, "pa_module_done"))) + goto fail; + + m->userdata = NULL; + m->core = c; + + assert(m->init); + if (m->init(c, m) < 0) + goto fail; + + if (!c->modules) + c->modules = pa_idxset_new(NULL, NULL); + + assert(c->modules); + r = pa_idxset_put(c->modules, m, &m->index); + assert(r >= 0 && m->index != PA_IDXSET_INVALID); + + fprintf(stderr, "module: loaded %u \"%s\" with argument \"%s\".\n", m->index, m->name, m->argument); + + return m; + +fail: + if (m) { + free(m->argument); + free(m->name); + + if (m->dl) + lt_dlclose(m->dl); + + free(m); + } + + return NULL; +} + +static void pa_module_free(struct pa_module *m) { + assert(m && m->done && m->core); + m->done(m->core, m); + + lt_dlclose(m->dl); + + fprintf(stderr, "module: unloaded %u \"%s\".\n", m->index, m->name); + + free(m->name); + free(m->argument); + free(m); +} + + +void pa_module_unload(struct pa_core *c, struct pa_module *m) { + assert(c && m); + + assert(c->modules); + if (!(m = pa_idxset_remove_by_data(c->modules, m, NULL))) + return; + + pa_module_free(m); +} + +void pa_module_unload_by_index(struct pa_core *c, uint32_t index) { + struct pa_module *m; + assert(c && index != PA_IDXSET_INVALID); + + assert(c->modules); + if (!(m = pa_idxset_remove_by_index(c->modules, index))) + return; + + pa_module_free(m); +} + +static void free_callback(void *p, void *userdata) { + struct pa_module *m = p; + assert(m); + pa_module_free(m); +} + +void pa_module_unload_all(struct pa_core *c) { + assert(c); + + if (!c->modules) + return; + + pa_idxset_free(c->modules, free_callback, NULL); + c->modules = NULL; +} + +struct once_info { + struct pa_core *core; + uint32_t index; +}; + + +static void module_unload_once_callback(void *userdata) { + struct once_info *i = userdata; + assert(i); + pa_module_unload_by_index(i->core, i->index); + free(i); +} + +void pa_module_unload_request(struct pa_core *c, struct pa_module *m) { + struct once_info *i; + assert(c && m); + + i = malloc(sizeof(struct once_info)); + assert(i); + i->core = c; + i->index = m->index; + pa_mainloop_api_once(c->mainloop, module_unload_once_callback, i); +} diff --git a/polyp/module.h b/polyp/module.h new file mode 100644 index 00000000..af2d8552 --- /dev/null +++ b/polyp/module.h @@ -0,0 +1,55 @@ +#ifndef foomodulehfoo +#define foomodulehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +#include "core.h" + +struct pa_module { + struct pa_core *core; + char *name, *argument; + uint32_t index; + + lt_dlhandle dl; + + int (*init)(struct pa_core *c, struct pa_module*m); + void (*done)(struct pa_core *c, struct pa_module*m); + + void *userdata; +}; + +struct pa_module* pa_module_load(struct pa_core *c, const char *name, const char*argument); +void pa_module_unload(struct pa_core *c, struct pa_module *m); +void pa_module_unload_by_index(struct pa_core *c, uint32_t index); + +void pa_module_unload_all(struct pa_core *c); + +void pa_module_unload_request(struct pa_core *c, struct pa_module *m); + +/* These to following prototypes are for module entrypoints and not implemented by the core */ +int pa_module_init(struct pa_core *c, struct pa_module*m); +void pa_module_done(struct pa_core *c, struct pa_module*m); + +#endif diff --git a/polyp/namereg.c b/polyp/namereg.c new file mode 100644 index 00000000..2349436f --- /dev/null +++ b/polyp/namereg.c @@ -0,0 +1,136 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "namereg.h" + +struct namereg_entry { + enum pa_namereg_type type; + char *name; + void *data; +}; + +void pa_namereg_free(struct pa_core *c) { + assert(c); + if (!c->namereg) + return; + assert(pa_hashmap_ncontents(c->namereg) == 0); + pa_hashmap_free(c->namereg, NULL, NULL); +} + +const char *pa_namereg_register(struct pa_core *c, const char *name, enum pa_namereg_type type, void *data, int fail) { + struct namereg_entry *e; + char *n = NULL; + int r; + + assert(c && name && data); + + if (!c->namereg) { + c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + assert(c->namereg); + } + + if ((e = pa_hashmap_get(c->namereg, name)) && fail) + return NULL; + + if (!e) + n = strdup(name); + else { + unsigned i; + size_t l = strlen(name); + n = malloc(l+3); + assert(n); + + for (i = 1; i <= 99; i++) { + snprintf(n, l+2, "%s%u", name, i); + + if (!(e = pa_hashmap_get(c->namereg, n))) + break; + } + + if (e) { + free(n); + return NULL; + } + } + + assert(n); + e = malloc(sizeof(struct namereg_entry)); + assert(e); + e->type = type; + e->name = n; + e->data = data; + + r = pa_hashmap_put(c->namereg, e->name, e); + assert (r >= 0); + + return e->name; + +} + +void pa_namereg_unregister(struct pa_core *c, const char *name) { + struct namereg_entry *e; + int r; + assert(c && name); + + e = pa_hashmap_get(c->namereg, name); + assert(e); + + r = pa_hashmap_remove(c->namereg, name); + assert(r >= 0); + + free(e->name); + free(e); +} + +void* pa_namereg_get(struct pa_core *c, const char *name, enum pa_namereg_type type) { + struct namereg_entry *e; + uint32_t index; + char *x = NULL; + void *d = NULL; + assert(c && name); + + if ((e = pa_hashmap_get(c->namereg, name))) + if (e->type == e->type) + return e->data; + + index = (uint32_t) strtol(name, &x, 0); + + if (!x || *x != 0) + return NULL; + + if (type == PA_NAMEREG_SINK) + d = pa_idxset_get_by_index(c->sinks, index); + else if (type == PA_NAMEREG_SOURCE) + d = pa_idxset_get_by_index(c->sources, index); + + return d; +} diff --git a/polyp/namereg.h b/polyp/namereg.h new file mode 100644 index 00000000..0af83cd8 --- /dev/null +++ b/polyp/namereg.h @@ -0,0 +1,38 @@ +#ifndef foonamereghfoo +#define foonamereghfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "core.h" + +enum pa_namereg_type { + PA_NAMEREG_SINK, + PA_NAMEREG_SOURCE +}; + +void pa_namereg_free(struct pa_core *c); + +const char *pa_namereg_register(struct pa_core *c, const char *name, enum pa_namereg_type type, void *data, int fail); +void pa_namereg_unregister(struct pa_core *c, const char *name); +void* pa_namereg_get(struct pa_core *c, const char *name, enum pa_namereg_type type); + +#endif diff --git a/polyp/native-common.h b/polyp/native-common.h new file mode 100644 index 00000000..2acbae8e --- /dev/null +++ b/polyp/native-common.h @@ -0,0 +1,68 @@ +#ifndef foonativecommonhfoo +#define foonativecommonhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +enum { + PA_COMMAND_ERROR, + PA_COMMAND_TIMEOUT, /* pseudo command */ + PA_COMMAND_REPLY, + PA_COMMAND_CREATE_PLAYBACK_STREAM, + PA_COMMAND_DELETE_PLAYBACK_STREAM, + PA_COMMAND_CREATE_RECORD_STREAM, + PA_COMMAND_DELETE_RECORD_STREAM, + PA_COMMAND_EXIT, + PA_COMMAND_REQUEST, + PA_COMMAND_AUTH, + PA_COMMAND_SET_NAME, + PA_COMMAND_LOOKUP_SINK, + PA_COMMAND_LOOKUP_SOURCE, + PA_COMMAND_DRAIN_PLAYBACK_STREAM, + PA_COMMAND_PLAYBACK_STREAM_KILLED, + PA_COMMAND_RECORD_STREAM_KILLED, + PA_COMMAND_STAT, + PA_COMMAND_GET_PLAYBACK_LATENCY, + PA_COMMAND_MAX +}; + +enum { + PA_ERROR_OK, + PA_ERROR_ACCESS, + PA_ERROR_COMMAND, + PA_ERROR_INVALID, + PA_ERROR_EXIST, + PA_ERROR_NOENTITY, + PA_ERROR_CONNECTIONREFUSED, + PA_ERROR_PROTOCOL, + PA_ERROR_TIMEOUT, + PA_ERROR_AUTHKEY, + PA_ERROR_INTERNAL, + PA_ERROR_CONNECTIONTERMINATED, + PA_ERROR_KILLED, + PA_ERROR_INVALIDSERVER, + PA_ERROR_MAX +}; + +#define PA_NATIVE_COOKIE_LENGTH 256 +#define PA_NATIVE_COOKIE_FILE ".polypaudio-cookie" + +#endif diff --git a/polyp/oss-util.c b/polyp/oss-util.c new file mode 100644 index 00000000..cf55a6ee --- /dev/null +++ b/polyp/oss-util.c @@ -0,0 +1,163 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "oss-util.h" + +int pa_oss_open(const char *device, int *mode, int* pcaps) { + int fd = -1; + assert(device && mode && (*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY)); + + if (*mode == O_RDWR) { + if ((fd = open(device, O_RDWR|O_NDELAY)) >= 0) { + int dcaps, *tcaps; + ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0); + + tcaps = pcaps ? pcaps : &dcaps; + + if (ioctl(fd, SNDCTL_DSP_GETCAPS, tcaps) < 0) { + fprintf(stderr, __FILE__": SNDCTL_DSP_GETCAPS: %s\n", strerror(errno)); + goto fail; + } + + if (*tcaps & DSP_CAP_DUPLEX) + return fd; + + close(fd); + } + + if ((fd = open(device, (*mode = O_WRONLY)|O_NDELAY)) < 0) { + if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY)) < 0) { + fprintf(stderr, __FILE__": open('%s'): %s\n", device, strerror(errno)); + goto fail; + } + } + } else { + if ((fd = open(device, *mode|O_NDELAY)) < 0) { + fprintf(stderr, __FILE__": open('%s'): %s\n", device, strerror(errno)); + goto fail; + } + } + + if (pcaps) { + if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) { + fprintf(stderr, "SNDCTL_DSP_GETCAPS: %s\n", strerror(errno)); + goto fail; + } + } + + return fd; + +fail: + if (fd >= 0) + close(fd); + return fd; +} + +int pa_oss_auto_format(int fd, struct pa_sample_spec *ss) { + int format, channels, speed, reqformat; + static const int format_trans[] = { + [PA_SAMPLE_U8] = AFMT_U8, + [PA_SAMPLE_ALAW] = AFMT_A_LAW, + [PA_SAMPLE_ULAW] = AFMT_MU_LAW, + [PA_SAMPLE_S16LE] = AFMT_S16_LE, + [PA_SAMPLE_S16BE] = AFMT_S16_BE, + [PA_SAMPLE_FLOAT32LE] = AFMT_QUERY, /* not supported */ + [PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */ + }; + + assert(fd >= 0 && ss); + + reqformat = format = format_trans[ss->format]; + if (reqformat == AFMT_QUERY || ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != reqformat) { + format = AFMT_S16_NE; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) { + int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE; + format = f; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) { + format = AFMT_U8; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) { + fprintf(stderr, "SNDCTL_DSP_SETFMT: %s\n", format != AFMT_U8 ? "No supported sample format" : strerror(errno)); + return -1; + } else + ss->format = PA_SAMPLE_U8; + } else + ss->format = f == AFMT_S16_LE ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE; + } else + ss->format = PA_SAMPLE_S16NE; + } + + channels = ss->channels; + if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) { + fprintf(stderr, "SNDCTL_DSP_CHANNELS: %s\n", strerror(errno)); + return -1; + } + assert(channels); + ss->channels = channels; + + speed = ss->rate; + if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) { + fprintf(stderr, "SNDCTL_DSP_SPEED: %s\n", strerror(errno)); + return -1; + } + assert(speed); + ss->rate = speed; + + return 0; +} + +static int log2(int v) { + int k = 0; + + for (;;) { + v >>= 1; + if (!v) break; + k++; + } + + return k; +} + +int pa_oss_set_fragments(int fd, int nfrags, int frag_size) { + int arg; + arg = ((int) nfrags << 16) | log2(frag_size); + + if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) { + fprintf(stderr, "SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno)); + return -1; + } + + return 0; +} diff --git a/polyp/oss-util.h b/polyp/oss-util.h new file mode 100644 index 00000000..cb2ce33c --- /dev/null +++ b/polyp/oss-util.h @@ -0,0 +1,32 @@ +#ifndef fooossutilhfoo +#define fooossutilhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "sample.h" + +int pa_oss_open(const char *device, int *mode, int* pcaps); +int pa_oss_auto_format(int fd, struct pa_sample_spec *ss); + +int pa_oss_set_fragments(int fd, int frags, int frag_size); + +#endif diff --git a/polyp/pacat-simple.c b/polyp/pacat-simple.c new file mode 100644 index 00000000..5018aa5f --- /dev/null +++ b/polyp/pacat-simple.c @@ -0,0 +1,83 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "polyplib-simple.h" +#include "polyplib-error.h" + +#define BUFSIZE 1024 + +int main(int argc, char*argv[]) { + static const struct pa_sample_spec ss = { + .format = PA_SAMPLE_S16LE, + .rate = 44100, + .channels = 2 + }; + struct pa_simple *s = NULL; + int ret = 1; + int error; + + if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, &error))) { + fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); + goto finish; + } + + for (;;) { + uint8_t buf[BUFSIZE]; + ssize_t r; + + if ((r = read(STDIN_FILENO, buf, sizeof(buf))) <= 0) { + if (r == 0) /* eof */ + break; + + fprintf(stderr, __FILE__": read() failed: %s\n", strerror(errno)); + goto finish; + } + + if (pa_simple_write(s, buf, r, &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error)); + goto finish; + } + } + + /* Make sure that every single sample way played */ + if (pa_simple_drain(s, &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error)); + goto finish; + } + + ret = 0; + +finish: + + if (s) + pa_simple_free(s); + + return ret; +} diff --git a/polyp/pacat.c b/polyp/pacat.c new file mode 100644 index 00000000..1d1cc3d5 --- /dev/null +++ b/polyp/pacat.c @@ -0,0 +1,336 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "polyplib.h" +#include "polyplib-error.h" +#include "mainloop.h" +#include "mainloop-signal.h" + +static enum { RECORD, PLAYBACK } mode = PLAYBACK; + +static struct pa_context *context = NULL; +static struct pa_stream *stream = NULL; +static struct pa_mainloop_api *mainloop_api = NULL; + +static void *buffer = NULL; +static size_t buffer_length = 0, buffer_index = 0; + +static void* stdio_source = NULL; + +static void quit(int ret) { + assert(mainloop_api); + mainloop_api->quit(mainloop_api, ret); +} + +static void context_die_callback(struct pa_context *c, void *userdata) { + assert(c); + fprintf(stderr, "Connection to server shut down, exiting.\n"); + quit(1); +} + +static void stream_die_callback(struct pa_stream *s, void *userdata) { + assert(s); + fprintf(stderr, "Stream deleted, exiting.\n"); + quit(1); +} + +static void do_stream_write(size_t length) { + size_t l; + assert(length); + + if (!buffer || !buffer_length) + return; + + l = length; + if (l > buffer_length) + l = buffer_length; + + pa_stream_write(stream, buffer+buffer_index, l); + buffer_length -= l; + buffer_index += l; + + if (!buffer_length) { + free(buffer); + buffer = NULL; + buffer_index = buffer_length = 0; + } +} + +static void stream_write_callback(struct pa_stream *s, size_t length, void *userdata) { + assert(s && length); + + if (stdio_source) + mainloop_api->enable_io(mainloop_api, stdio_source, PA_MAINLOOP_API_IO_EVENT_INPUT); + + if (!buffer) + return; + + do_stream_write(length); +} + +static void stream_read_callback(struct pa_stream *s, const void*data, size_t length, void *userdata) { + assert(s && data && length); + + if (stdio_source) + mainloop_api->enable_io(mainloop_api, stdio_source, PA_MAINLOOP_API_IO_EVENT_OUTPUT); + + if (buffer) { + fprintf(stderr, "Buffer overrrun, dropping incoming data\n"); + return; + } + + buffer = malloc(buffer_length = length); + assert(buffer); + memcpy(buffer, data, length); + buffer_index = 0; +} + +static void stream_complete_callback(struct pa_stream*s, int success, void *userdata) { + assert(s); + + if (!success) { + fprintf(stderr, "Stream creation failed: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s)))); + quit(1); + return; + } + + fprintf(stderr, "Stream created.\n"); +} + +static void context_complete_callback(struct pa_context *c, int success, void *userdata) { + static const struct pa_sample_spec ss = { + .format = PA_SAMPLE_S16LE, + .rate = 44100, + .channels = 2 + }; + + assert(c && !stream); + + if (!success) { + fprintf(stderr, "Connection failed: %s\n", pa_strerror(pa_context_errno(c))); + goto fail; + } + + fprintf(stderr, "Connection established.\n"); + + if (!(stream = pa_stream_new(c, mode == PLAYBACK ? PA_STREAM_PLAYBACK : PA_STREAM_RECORD, NULL, "pacat", &ss, NULL, stream_complete_callback, NULL))) { + fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(c))); + goto fail; + } + + pa_stream_set_die_callback(stream, stream_die_callback, NULL); + pa_stream_set_write_callback(stream, stream_write_callback, NULL); + pa_stream_set_read_callback(stream, stream_read_callback, NULL); + + return; + +fail: + quit(1); +} + +static void context_drain_complete(struct pa_context*c, void *userdata) { + quit(0); +} + +static void stream_drain_complete(struct pa_stream*s, void *userdata) { + fprintf(stderr, "Playback stream drained.\n"); + + pa_stream_free(stream); + stream = NULL; + + if (pa_context_drain(context, context_drain_complete, NULL) < 0) + quit(0); + else + fprintf(stderr, "Draining connection to server.\n"); +} + +static void stdin_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { + size_t l, w = 0; + ssize_t r; + assert(a == mainloop_api && id && stdio_source == id); + + if (buffer) { + mainloop_api->enable_io(mainloop_api, stdio_source, PA_MAINLOOP_API_IO_EVENT_NULL); + return; + } + + if (!stream || !pa_stream_is_ready(stream) || !(l = w = pa_stream_writable_size(stream))) + l = 4096; + + buffer = malloc(l); + assert(buffer); + if ((r = read(fd, buffer, l)) <= 0) { + if (r == 0) { + fprintf(stderr, "Got EOF.\n"); + pa_stream_drain(stream, stream_drain_complete, NULL); + } else { + fprintf(stderr, "read() failed: %s\n", strerror(errno)); + quit(1); + } + + mainloop_api->cancel_io(mainloop_api, stdio_source); + stdio_source = NULL; + return; + } + + buffer_length = r; + buffer_index = 0; + + if (w) + do_stream_write(w); +} + +static void stdout_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { + ssize_t r; + assert(a == mainloop_api && id && stdio_source == id); + + if (!buffer) { + mainloop_api->enable_io(mainloop_api, stdio_source, PA_MAINLOOP_API_IO_EVENT_NULL); + return; + } + + assert(buffer_length); + + if ((r = write(fd, buffer+buffer_index, buffer_length)) <= 0) { + fprintf(stderr, "write() failed: %s\n", strerror(errno)); + quit(1); + + mainloop_api->cancel_io(mainloop_api, stdio_source); + stdio_source = NULL; + return; + } + + buffer_length -= r; + buffer_index += r; + + if (!buffer_length) { + free(buffer); + buffer = NULL; + buffer_length = buffer_index = 0; + } +} + +static void exit_signal_callback(void *id, int sig, void *userdata) { + fprintf(stderr, "Got SIGINT, exiting.\n"); + quit(0); + +} + +static void stream_get_latency_callback(struct pa_stream *s, uint32_t latency, void *userdata) { + assert(s); + + if (latency == (uint32_t) -1) { + fprintf(stderr, "Failed to get latency: %s\n", strerror(errno)); + quit(1); + return; + } + + fprintf(stderr, "Current latency is %u usecs.\n", latency); +} + +static void sigusr1_signal_callback(void *id, int sig, void *userdata) { + if (mode == PLAYBACK) { + fprintf(stderr, "Got SIGUSR1, requesting latency.\n"); + pa_stream_get_latency(stream, stream_get_latency_callback, NULL); + } +} + +int main(int argc, char *argv[]) { + struct pa_mainloop* m = NULL; + int ret = 1, r; + char *bn; + + if (!(bn = strrchr(argv[0], '/'))) + bn = argv[0]; + + if (strstr(bn, "rec") || strstr(bn, "mon")) + mode = RECORD; + else if (strstr(bn, "cat") || strstr(bn, "play")) + mode = PLAYBACK; + + fprintf(stderr, "Opening a %s stream.\n", mode == RECORD ? "recording" : "playback"); + + if (!(m = pa_mainloop_new())) { + fprintf(stderr, "pa_mainloop_new() failed.\n"); + goto quit; + } + + mainloop_api = pa_mainloop_get_api(m); + + r = pa_signal_init(mainloop_api); + assert(r == 0); + pa_signal_register(SIGINT, exit_signal_callback, NULL); + pa_signal_register(SIGUSR1, sigusr1_signal_callback, NULL); + signal(SIGPIPE, SIG_IGN); + + if (!(stdio_source = mainloop_api->source_io(mainloop_api, + mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO, + mode == PLAYBACK ? PA_MAINLOOP_API_IO_EVENT_INPUT : PA_MAINLOOP_API_IO_EVENT_OUTPUT, + mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) { + fprintf(stderr, "source_io() failed.\n"); + goto quit; + } + + if (!(context = pa_context_new(mainloop_api, argv[0]))) { + fprintf(stderr, "pa_context_new() failed.\n"); + goto quit; + } + + if (pa_context_connect(context, NULL, context_complete_callback, NULL) < 0) { + fprintf(stderr, "pa_context_connext() failed.\n"); + goto quit; + } + + pa_context_set_die_callback(context, context_die_callback, NULL); + + if (pa_mainloop_run(m, &ret) < 0) { + fprintf(stderr, "pa_mainloop_run() failed.\n"); + goto quit; + } + +quit: + if (stream) + pa_stream_free(stream); + if (context) + pa_context_free(context); + + if (m) { + pa_signal_done(); + pa_mainloop_free(m); + } + + if (buffer) + free(buffer); + + return ret; +} diff --git a/polyp/packet.c b/polyp/packet.c new file mode 100644 index 00000000..e94df057 --- /dev/null +++ b/polyp/packet.c @@ -0,0 +1,72 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "packet.h" + +struct pa_packet* pa_packet_new(size_t length) { + struct pa_packet *p; + assert(length); + p = malloc(sizeof(struct pa_packet)+length); + assert(p); + + p->ref = 1; + p->length = length; + p->data = (uint8_t*) (p+1); + p->type = PA_PACKET_APPENDED; + return p; +} + +struct pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length) { + struct pa_packet *p; + assert(data && length); + p = malloc(sizeof(struct pa_packet)); + assert(p); + + p->ref = 1; + p->length = length; + p->data = data; + p->type = PA_PACKET_DYNAMIC; + return p; +} + +struct pa_packet* pa_packet_ref(struct pa_packet *p) { + assert(p && p->ref >= 1); + p->ref++; + return p; +} + +void pa_packet_unref(struct pa_packet *p) { + assert(p && p->ref >= 1); + p->ref--; + + if (p->ref == 0) { + if (p->type == PA_PACKET_DYNAMIC) + free(p->data); + free(p); + } +} diff --git a/polyp/packet.h b/polyp/packet.h new file mode 100644 index 00000000..8bea07e1 --- /dev/null +++ b/polyp/packet.h @@ -0,0 +1,41 @@ +#ifndef foopackethfoo +#define foopackethfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +struct pa_packet { + enum { PA_PACKET_APPENDED, PA_PACKET_DYNAMIC } type; + unsigned ref; + size_t length; + uint8_t *data; +}; + +struct pa_packet* pa_packet_new(size_t length); +struct pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length); + +struct pa_packet* pa_packet_ref(struct pa_packet *p); +void pa_packet_unref(struct pa_packet *p); + +#endif diff --git a/polyp/pactl.c b/polyp/pactl.c new file mode 100644 index 00000000..2b1ed2c1 --- /dev/null +++ b/polyp/pactl.c @@ -0,0 +1,166 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "polyplib.h" +#include "polyplib-error.h" +#include "mainloop.h" +#include "mainloop-signal.h" + +static struct pa_context *context = NULL; +static struct pa_mainloop_api *mainloop_api = NULL; + +static enum { + NONE, + EXIT, + STAT +} action = NONE; + +static void quit(int ret) { + assert(mainloop_api); + mainloop_api->quit(mainloop_api, ret); +} + +static void context_die_callback(struct pa_context *c, void *userdata) { + assert(c); + fprintf(stderr, "Connection to server shut down, exiting.\n"); + quit(1); +} + +static void context_drain_complete(struct pa_context *c, void *userdata) { + assert(c); + fprintf(stderr, "Connection to server shut down, exiting.\n"); + quit(0); +} + +static void drain(void) { + if (pa_context_drain(context, context_drain_complete, NULL) < 0) + quit(0); +} + +static void stat_callback(struct pa_context *c, uint32_t blocks, uint32_t total, void *userdata) { + if (blocks == (uint32_t) -1) { + fprintf(stderr, "Failed to get statistics: %s\n", pa_strerror(pa_context_errno(c))); + quit(1); + return; + } + + fprintf(stderr, "Currently in use: %u blocks containing %u bytes total.\n", blocks, total); + drain(); +} + +static void context_complete_callback(struct pa_context *c, int success, void *userdata) { + assert(c); + + if (!success) { + fprintf(stderr, "Connection failed: %s\n", pa_strerror(pa_context_errno(c))); + goto fail; + } + + fprintf(stderr, "Connection established.\n"); + + if (action == STAT) + pa_context_stat(c, stat_callback, NULL); + else { + assert(action == EXIT); + pa_context_exit(c); + drain(); + } + + return; + +fail: + quit(1); +} + +static void exit_signal_callback(void *id, int sig, void *userdata) { + fprintf(stderr, "Got SIGINT, exiting.\n"); + quit(0); + +} + +int main(int argc, char *argv[]) { + struct pa_mainloop* m = NULL; + int ret = 1, r; + + if (argc >= 2) { + if (!strcmp(argv[1], "stat")) + action = STAT; + else if (!strcmp(argv[1], "exit")) + action = EXIT; + } + + if (action == NONE) { + fprintf(stderr, "No valid action specified. Use one of: stat, exit\n"); + goto quit; + } + + if (!(m = pa_mainloop_new())) { + fprintf(stderr, "pa_mainloop_new() failed.\n"); + goto quit; + } + + mainloop_api = pa_mainloop_get_api(m); + + r = pa_signal_init(mainloop_api); + assert(r == 0); + pa_signal_register(SIGINT, exit_signal_callback, NULL); + signal(SIGPIPE, SIG_IGN); + + if (!(context = pa_context_new(mainloop_api, argv[0]))) { + fprintf(stderr, "pa_context_new() failed.\n"); + goto quit; + } + + if (pa_context_connect(context, NULL, context_complete_callback, NULL) < 0) { + fprintf(stderr, "pa_context_connext() failed.\n"); + goto quit; + } + + pa_context_set_die_callback(context, context_die_callback, NULL); + + if (pa_mainloop_run(m, &ret) < 0) { + fprintf(stderr, "pa_mainloop_run() failed.\n"); + goto quit; + } + +quit: + if (context) + pa_context_free(context); + + if (m) { + pa_signal_done(); + pa_mainloop_free(m); + } + + return ret; +} diff --git a/polyp/parec-simple.c b/polyp/parec-simple.c new file mode 100644 index 00000000..c4196a14 --- /dev/null +++ b/polyp/parec-simple.c @@ -0,0 +1,94 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "polyplib-simple.h" +#include "polyplib-error.h" + +#define BUFSIZE 1024 + +static ssize_t loop_write(int fd, const void*data, size_t size) { + ssize_t ret = 0; + + while (size > 0) { + ssize_t r; + + if ((r = write(fd, data, size)) < 0) + return r; + + if (r == 0) + break; + + ret += r; + data += r; + size -= r; + } + + return ret; +} + +int main(int argc, char*argv[]) { + static const struct pa_sample_spec ss = { + .format = PA_SAMPLE_S16LE, + .rate = 44100, + .channels = 2 + }; + struct pa_simple *s = NULL; + int ret = 1; + int error; + + if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "record", &ss, NULL, &error))) { + fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); + goto finish; + } + + for (;;) { + uint8_t buf[BUFSIZE]; + ssize_t r; + + if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error)); + goto finish; + } + + if ((r = loop_write(STDOUT_FILENO, buf, sizeof(buf))) <= 0) { + fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno)); + goto finish; + } + } + + ret = 0; + +finish: + + if (s) + pa_simple_free(s); + + return ret; +} diff --git a/polyp/pdispatch.c b/polyp/pdispatch.c new file mode 100644 index 00000000..a28458a4 --- /dev/null +++ b/polyp/pdispatch.c @@ -0,0 +1,247 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "pdispatch.h" +#include "native-common.h" + +#ifdef DEBUG_OPCODES + +static const char *command_names[PA_COMMAND_MAX] = { + [PA_COMMAND_ERROR] = "ERROR", + [PA_COMMAND_TIMEOUT] = "TIMEOUT", + [PA_COMMAND_REPLY] = "REPLY", + [PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM", + [PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM", + [PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM", + [PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM", + [PA_COMMAND_AUTH] = "AUTH", + [PA_COMMAND_REQUEST] = "REQUEST", + [PA_COMMAND_EXIT] = "EXIT", + [PA_COMMAND_SET_NAME] = "SET_NAME", + [PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK", + [PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE", + [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM", + [PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED", + [PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED", + [PA_COMMAND_STAT] = "STAT", + [PA_COMMAND_GET_PLAYBACK_LATENCY] = "PLAYBACK_LATENCY", +}; + +#endif + +struct reply_info { + struct pa_pdispatch *pdispatch; + struct reply_info *next, *previous; + void (*callback)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); + void *userdata; + uint32_t tag; + void *mainloop_timeout; + int callback_is_running; +}; + +struct pa_pdispatch { + struct pa_mainloop_api *mainloop; + const struct pa_pdispatch_command *command_table; + unsigned n_commands; + struct reply_info *replies; + void (*drain_callback)(struct pa_pdispatch *pd, void *userdata); + void *drain_userdata; + int in_use, shall_free; +}; + +static void reply_info_free(struct reply_info *r) { + assert(r && r->pdispatch && r->pdispatch->mainloop); + + if (r->pdispatch) + r->pdispatch->mainloop->cancel_time(r->pdispatch->mainloop, r->mainloop_timeout); + + if (r->previous) + r->previous->next = r->next; + else + r->pdispatch->replies = r->next; + + if (r->next) + r->next->previous = r->previous; + + free(r); +} + +struct pa_pdispatch* pa_pdispatch_new(struct pa_mainloop_api *mainloop, const struct pa_pdispatch_command*table, unsigned entries) { + struct pa_pdispatch *pd; + assert(mainloop); + + assert((entries && table) || (!entries && !table)); + + pd = malloc(sizeof(struct pa_pdispatch)); + assert(pd); + pd->mainloop = mainloop; + pd->command_table = table; + pd->n_commands = entries; + pd->replies = NULL; + pd->drain_callback = NULL; + pd->drain_userdata = NULL; + + pd->in_use = pd->shall_free = 0; + return pd; +} + +void pa_pdispatch_free(struct pa_pdispatch *pd) { + assert(pd); + + if (pd->in_use) { + pd->shall_free = 1; + return; + } + + while (pd->replies) + reply_info_free(pd->replies); + free(pd); +} + +int pa_pdispatch_run(struct pa_pdispatch *pd, struct pa_packet*packet, void *userdata) { + uint32_t tag, command; + struct pa_tagstruct *ts = NULL; + int ret = -1; + assert(pd && packet && packet->data && !pd->in_use); + + if (packet->length <= 8) + goto finish; + + ts = pa_tagstruct_new(packet->data, packet->length); + assert(ts); + + if (pa_tagstruct_getu32(ts, &command) < 0 || + pa_tagstruct_getu32(ts, &tag) < 0) + goto finish; + +#ifdef DEBUG_OPCODES + fprintf(stderr, __FILE__": Recieved opcode <%s>\n", command_names[command]); +#endif + + if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) { + struct reply_info *r; + + for (r = pd->replies; r; r = r->next) + if (r->tag == tag) + break; + + if (r) { + pd->in_use = r->callback_is_running = 1; + assert(r->callback); + r->callback(r->pdispatch, command, tag, ts, r->userdata); + pd->in_use = r->callback_is_running = 0; + reply_info_free(r); + + if (pd->shall_free) + pa_pdispatch_free(pd); + else { + if (pd->drain_callback && !pa_pdispatch_is_pending(pd)) + pd->drain_callback(pd, pd->drain_userdata); + } + } + + } else if (pd->command_table && command < pd->n_commands) { + const struct pa_pdispatch_command *c = pd->command_table+command; + + if (c->proc) + c->proc(pd, command, tag, ts, userdata); + } else + goto finish; + + ret = 0; + +finish: + if (ts) + pa_tagstruct_free(ts); + + return ret; +} + +static void timeout_callback(struct pa_mainloop_api*m, void *id, const struct timeval *tv, void *userdata) { + struct reply_info*r = userdata; + assert (r && r->mainloop_timeout == id && r->pdispatch && r->pdispatch->mainloop == m && r->callback); + + r->callback(r->pdispatch, PA_COMMAND_TIMEOUT, r->tag, NULL, r->userdata); + reply_info_free(r); + + if (r->pdispatch->drain_callback && !pa_pdispatch_is_pending(r->pdispatch)) + r->pdispatch->drain_callback(r->pdispatch, r->pdispatch->drain_userdata); +} + +void pa_pdispatch_register_reply(struct pa_pdispatch *pd, uint32_t tag, int timeout, void (*cb)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata), void *userdata) { + struct reply_info *r; + struct timeval tv; + assert(pd && cb); + + r = malloc(sizeof(struct reply_info)); + assert(r); + r->pdispatch = pd; + r->callback = cb; + r->userdata = userdata; + r->tag = tag; + r->callback_is_running = 0; + + gettimeofday(&tv, NULL); + tv.tv_sec += timeout; + + r->mainloop_timeout = pd->mainloop->source_time(pd->mainloop, &tv, timeout_callback, r); + assert(r->mainloop_timeout); + + r->previous = NULL; + r->next = pd->replies; + if (r->next) + r->next->previous = r; + pd->replies = r; +} + +int pa_pdispatch_is_pending(struct pa_pdispatch *pd) { + assert(pd); + + return !!pd->replies; +} + +void pa_pdispatch_set_drain_callback(struct pa_pdispatch *pd, void (*cb)(struct pa_pdispatch *pd, void *userdata), void *userdata) { + assert(pd); + assert(!cb || pa_pdispatch_is_pending(pd)); + + pd->drain_callback = cb; + pd->drain_userdata = userdata; +} + +void pa_pdispatch_unregister_reply(struct pa_pdispatch *pd, void *userdata) { + struct reply_info *r, *n; + assert(pd); + + for (r = pd->replies; r; r = n) { + n = r->next; + + if (!r->callback_is_running && r->userdata == userdata) /* when this item's callback is currently running it is destroyed anyway in the very near future */ + reply_info_free(r); + } +} diff --git a/polyp/pdispatch.h b/polyp/pdispatch.h new file mode 100644 index 00000000..796c1e03 --- /dev/null +++ b/polyp/pdispatch.h @@ -0,0 +1,52 @@ +#ifndef foopdispatchhfoo +#define foopdispatchhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include "tagstruct.h" +#include "packet.h" +#include "mainloop-api.h" + +struct pa_pdispatch; + +/* It is safe to destroy the calling pdispatch object from all callbacks */ + +struct pa_pdispatch_command { + void (*proc)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +}; + +struct pa_pdispatch* pa_pdispatch_new(struct pa_mainloop_api *m, const struct pa_pdispatch_command*table, unsigned entries); +void pa_pdispatch_free(struct pa_pdispatch *pd); + +int pa_pdispatch_run(struct pa_pdispatch *pd, struct pa_packet*p, void *userdata); + +void pa_pdispatch_register_reply(struct pa_pdispatch *pd, uint32_t tag, int timeout, void (*cb)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata), void *userdata); + +int pa_pdispatch_is_pending(struct pa_pdispatch *pd); + +void pa_pdispatch_set_drain_callback(struct pa_pdispatch *pd, void (*cb)(struct pa_pdispatch *pd, void *userdata), void *userdata); + +/* Remove all reply slots with the give userdata parameter */ +void pa_pdispatch_unregister_reply(struct pa_pdispatch *pd, void *userdata); + +#endif diff --git a/polyp/polypaudio.pa b/polyp/polypaudio.pa new file mode 100755 index 00000000..177707ba --- /dev/null +++ b/polyp/polypaudio.pa @@ -0,0 +1,41 @@ +#!./polypaudio -F + +# +# This file is part of polypaudio. +# +# polypaudio is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# polypaudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with polypaudio; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + +# Load audio drivers +load module-alsa-sink +load module-alsa-source device=plughw:1,0 +#load module-oss device="/dev/dsp" +#load module-oss-mmap device="/dev/dsp" + +# Load several protocols +load module-esound-protocol-tcp +load module-simple-protocol-tcp +load module-native-protocol-unix +load module-cli-protocol-unix + +# Load the CLI module +load module-cli + +.nofail + +# Make some devices default +sink_default alsa_output +source_default alsa_input + diff --git a/polyp/polyplib-def.h b/polyp/polyplib-def.h new file mode 100644 index 00000000..e43f4b35 --- /dev/null +++ b/polyp/polyplib-def.h @@ -0,0 +1,40 @@ +#ifndef foopolyplibdefhfoo +#define foopolyplibdefhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +enum pa_stream_direction { + PA_STREAM_PLAYBACK, + PA_STREAM_RECORD +}; + +struct pa_buffer_attr { + uint32_t maxlength; + uint32_t tlength; + uint32_t prebuf; + uint32_t minreq; + uint32_t fragsize; +}; + +#endif diff --git a/polyp/polyplib-error.c b/polyp/polyplib-error.c new file mode 100644 index 00000000..10b637c4 --- /dev/null +++ b/polyp/polyplib-error.c @@ -0,0 +1,53 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#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 corrupt", + [PA_ERROR_TIMEOUT] = "Timeout", + [PA_ERROR_AUTHKEY] = "Not authorization key", + [PA_ERROR_INTERNAL] = "Internal error", + [PA_ERROR_CONNECTIONTERMINATED] = "Connection terminated", + [PA_ERROR_KILLED] = "Entity killed", +}; + +const char*pa_strerror(uint32_t error) { + if (error >= PA_ERROR_MAX) + return NULL; + + return errortab[error]; +} diff --git a/polyp/polyplib-error.h b/polyp/polyplib-error.h new file mode 100644 index 00000000..cb86864a --- /dev/null +++ b/polyp/polyplib-error.h @@ -0,0 +1,29 @@ +#ifndef foopolypliberrorhfoo +#define foopolypliberrorhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +const char* pa_strerror(uint32_t error); + +#endif diff --git a/polyp/polyplib-simple.c b/polyp/polyplib-simple.c new file mode 100644 index 00000000..024cb18a --- /dev/null +++ b/polyp/polyplib-simple.c @@ -0,0 +1,259 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "polyplib-simple.h" +#include "polyplib.h" +#include "mainloop.h" +#include "native-common.h" +/*#include "polyp-error.h"*/ + +struct pa_simple { + struct pa_mainloop *mainloop; + struct pa_context *context; + struct pa_stream *stream; + enum pa_stream_direction direction; + + int dead, drained; + + void *read_data; + size_t read_index, read_length; +}; + +static void read_callback(struct pa_stream *s, const void*data, size_t length, void *userdata); + +static int check_error(struct pa_simple *p, int *perror) { + assert(p); + + if (pa_context_is_dead(p->context) || (p->stream && pa_stream_is_dead(p->stream))) { + if (perror) + *perror = pa_context_errno(p->context); + return -1; + } + + return 0; +} + +static int iterate(struct pa_simple *p, int block, int *perror) { + assert(p && p->context && p->mainloop); + + if (check_error(p, perror) < 0) + return -1; + + if (!block && !pa_context_is_pending(p->context)) + return 0; + + do { + if (pa_mainloop_iterate(p->mainloop, 1, NULL) < 0) { + if (perror) + *perror = PA_ERROR_INTERNAL; + return -1; + } + + if (check_error(p, perror) < 0) + return -1; + + } while (pa_context_is_pending(p->context)); + + return 0; +} + +struct pa_simple* pa_simple_new( + const char *server, + const char *name, + enum pa_stream_direction dir, + const char *dev, + const char *stream_name, + const struct pa_sample_spec *ss, + const struct pa_buffer_attr *attr, + int *perror) { + + struct pa_simple *p; + int error = PA_ERROR_INTERNAL; + assert(ss); + + p = malloc(sizeof(struct pa_simple)); + assert(p); + p->context = NULL; + p->stream = NULL; + p->mainloop = pa_mainloop_new(); + assert(p->mainloop); + p->dead = 0; + p->direction = dir; + p->read_data = NULL; + p->read_index = p->read_length = 0; + + if (!(p->context = pa_context_new(pa_mainloop_get_api(p->mainloop), name))) + goto fail; + + if (pa_context_connect(p->context, server, NULL, NULL) < 0) { + error = pa_context_errno(p->context); + goto fail; + } + + /* Wait until the context is ready */ + while (!pa_context_is_ready(p->context)) { + if (iterate(p, 1, &error) < 0) + goto fail; + } + + if (!(p->stream = pa_stream_new(p->context, dir, dev, stream_name, ss, attr, NULL, NULL))) + goto fail; + + /* Wait until the stream is ready */ + while (!pa_stream_is_ready(p->stream)) { + if (iterate(p, 1, &error) < 0) + goto fail; + } + + pa_stream_set_read_callback(p->stream, read_callback, p); + + return p; + +fail: + if (perror) + *perror = error; + pa_simple_free(p); + return NULL; +} + +void pa_simple_free(struct pa_simple *s) { + assert(s); + + free(s->read_data); + + if (s->stream) + pa_stream_free(s->stream); + + if (s->context) + pa_context_free(s->context); + + if (s->mainloop) + pa_mainloop_free(s->mainloop); + + free(s); +} + +int pa_simple_write(struct pa_simple *p, const void*data, size_t length, int *perror) { + assert(p && data && p->direction == PA_STREAM_PLAYBACK); + + while (length > 0) { + size_t l; + + while (!(l = pa_stream_writable_size(p->stream))) + if (iterate(p, 1, perror) < 0) + return -1; + + if (l > length) + l = length; + + pa_stream_write(p->stream, data, l); + data += l; + length -= l; + } + + /* Make sure that no data is pending for write */ + if (iterate(p, 0, perror) < 0) + return -1; + + return 0; +} + +static void read_callback(struct pa_stream *s, const void*data, size_t length, void *userdata) { + struct pa_simple *p = userdata; + assert(s && data && length && p); + + if (p->read_data) { + fprintf(stderr, __FILE__": Buffer overflow, dropping incoming memory blocks.\n"); + free(p->read_data); + } + + p->read_data = malloc(p->read_length = length); + assert(p->read_data); + memcpy(p->read_data, data, length); + p->read_index = 0; +} + +int pa_simple_read(struct pa_simple *p, void*data, size_t length, int *perror) { + assert(p && data && p->direction == PA_STREAM_RECORD); + + while (length > 0) { + if (p->read_data) { + size_t l = length; + + if (p->read_length <= l) + l = p->read_length; + + memcpy(data, p->read_data+p->read_index, l); + + data += l; + length -= l; + + p->read_index += l; + p->read_length -= l; + + if (!p->read_length) { + free(p->read_data); + p->read_data = NULL; + p->read_index = 0; + } + + if (!length) + return 0; + + assert(!p->read_data); + } + + if (iterate(p, 1, perror) < 0) + return -1; + } + + return 0; +} + +static void drain_complete(struct pa_stream *s, void *userdata) { + struct pa_simple *p = userdata; + assert(s && p); + p->drained = 1; +} + +int pa_simple_drain(struct pa_simple *p, int *perror) { + assert(p && p->direction == PA_STREAM_PLAYBACK); + p->drained = 0; + pa_stream_drain(p->stream, drain_complete, p); + + while (!p->drained) { + if (iterate(p, 1, perror) < 0) { + pa_stream_drain(p->stream, NULL, NULL); + return -1; + } + } + + return 0; +} diff --git a/polyp/polyplib-simple.h b/polyp/polyplib-simple.h new file mode 100644 index 00000000..0544410c --- /dev/null +++ b/polyp/polyplib-simple.h @@ -0,0 +1,49 @@ +#ifndef foopolyplibsimplehfoo +#define foopolyplibsimplehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "sample.h" +#include "polyplib-def.h" + +struct pa_simple; + +struct pa_simple* pa_simple_new( + const char *server, + const char *name, + enum pa_stream_direction dir, + const char *dev, + const char *stream_name, + const struct pa_sample_spec *ss, + const struct pa_buffer_attr *attr, + int *error); + +void pa_simple_free(struct pa_simple *s); + +int pa_simple_write(struct pa_simple *s, const void*data, size_t length, int *error); +int pa_simple_drain(struct pa_simple *s, int *error); + +int pa_simple_read(struct pa_simple *s, void*data, size_t length, int *error); + +#endif diff --git a/polyp/polyplib.c b/polyp/polyplib.c new file mode 100644 index 00000000..ea5003b4 --- /dev/null +++ b/polyp/polyplib.c @@ -0,0 +1,973 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "polyplib.h" +#include "native-common.h" +#include "pdispatch.h" +#include "pstream.h" +#include "dynarray.h" +#include "socket-client.h" +#include "pstream-util.h" +#include "authkey.h" +#include "util.h" + +#define DEFAULT_MAXLENGTH 204800 +#define DEFAULT_TLENGTH 10240 +#define DEFAULT_PREBUF 4096 +#define DEFAULT_MINREQ 1024 +#define DEFAULT_FRAGSIZE 1024 + +#define DEFAULT_TIMEOUT (5*60) +#define DEFAULT_SERVER "/tmp/polypaudio/native" +#define DEFAULT_PORT "4713" + +struct pa_context { + char *name; + struct pa_mainloop_api* mainloop; + struct pa_socket_client *client; + struct pa_pstream *pstream; + struct pa_pdispatch *pdispatch; + struct pa_dynarray *record_streams, *playback_streams; + struct pa_stream *first_stream; + uint32_t ctag; + uint32_t error; + enum { + CONTEXT_UNCONNECTED, + CONTEXT_CONNECTING, + CONTEXT_AUTHORIZING, + CONTEXT_SETTING_NAME, + CONTEXT_READY, + CONTEXT_DEAD + } state; + + void (*connect_complete_callback)(struct pa_context*c, int success, void *userdata); + void *connect_complete_userdata; + + void (*drain_complete_callback)(struct pa_context*c, void *userdata); + void *drain_complete_userdata; + + void (*die_callback)(struct pa_context*c, void *userdata); + void *die_userdata; + + void (*stat_callback)(struct pa_context*c, uint32_t count, uint32_t total, void *userdata); + void *stat_userdata; + + uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH]; +}; + +struct pa_stream { + struct pa_context *context; + struct pa_stream *next, *previous; + + char *name; + struct pa_buffer_attr buffer_attr; + struct pa_sample_spec sample_spec; + uint32_t device_index; + uint32_t channel; + int channel_valid; + enum pa_stream_direction direction; + + enum { STREAM_LOOKING_UP, STREAM_CREATING, STREAM_READY, STREAM_DEAD} state; + uint32_t requested_bytes; + + void (*read_callback)(struct pa_stream *p, const void*data, size_t length, void *userdata); + void *read_userdata; + + void (*write_callback)(struct pa_stream *p, size_t length, void *userdata); + void *write_userdata; + + void (*create_complete_callback)(struct pa_stream *s, int success, void *userdata); + void *create_complete_userdata; + + void (*drain_complete_callback)(struct pa_stream *s, void *userdata); + void *drain_complete_userdata; + + void (*die_callback)(struct pa_stream*c, void *userdata); + void *die_userdata; + + void (*get_latency_callback)(struct pa_stream*c, uint32_t latency, void *userdata); + void *get_latency_userdata; +}; + +static void command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_playback_stream_killed(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); + +static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = { + [PA_COMMAND_ERROR] = { NULL }, + [PA_COMMAND_REPLY] = { NULL }, + [PA_COMMAND_CREATE_PLAYBACK_STREAM] = { NULL }, + [PA_COMMAND_DELETE_PLAYBACK_STREAM] = { NULL }, + [PA_COMMAND_CREATE_RECORD_STREAM] = { NULL }, + [PA_COMMAND_DELETE_RECORD_STREAM] = { NULL }, + [PA_COMMAND_EXIT] = { NULL }, + [PA_COMMAND_REQUEST] = { command_request }, + [PA_COMMAND_PLAYBACK_STREAM_KILLED] = { command_playback_stream_killed }, + [PA_COMMAND_RECORD_STREAM_KILLED] = { command_playback_stream_killed }, +}; + +struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *name) { + struct pa_context *c; + assert(mainloop && name); + + c = malloc(sizeof(struct pa_context)); + assert(c); + c->name = strdup(name); + c->mainloop = mainloop; + c->client = NULL; + c->pstream = NULL; + c->pdispatch = NULL; + c->playback_streams = pa_dynarray_new(); + assert(c->playback_streams); + c->record_streams = pa_dynarray_new(); + assert(c->record_streams); + c->first_stream = NULL; + c->error = PA_ERROR_OK; + c->state = CONTEXT_UNCONNECTED; + c->ctag = 0; + + c->connect_complete_callback = NULL; + c->connect_complete_userdata = NULL; + + c->drain_complete_callback = NULL; + c->drain_complete_userdata = NULL; + + c->die_callback = NULL; + c->die_userdata = NULL; + + c->stat_callback = NULL; + c->stat_userdata = NULL; + + pa_check_for_sigpipe(); + return c; +} + +void pa_context_free(struct pa_context *c) { + assert(c); + + while (c->first_stream) + pa_stream_free(c->first_stream); + + if (c->client) + pa_socket_client_free(c->client); + if (c->pdispatch) + pa_pdispatch_free(c->pdispatch); + if (c->pstream) + pa_pstream_free(c->pstream); + if (c->record_streams) + pa_dynarray_free(c->record_streams, NULL, NULL); + if (c->playback_streams) + pa_dynarray_free(c->playback_streams, NULL, NULL); + + free(c->name); + free(c); +} + +static void stream_dead(struct pa_stream *s) { + assert(s); + + if (s->state == STREAM_DEAD) + return; + + if (s->state == STREAM_READY) { + s->state = STREAM_DEAD; + if (s->die_callback) + s->die_callback(s, s->die_userdata); + } else + s->state = STREAM_DEAD; +} + +static void context_dead(struct pa_context *c) { + struct pa_stream *s; + assert(c); + + if (c->state == CONTEXT_DEAD) + return; + + if (c->pdispatch) + pa_pdispatch_free(c->pdispatch); + c->pdispatch = NULL; + + if (c->pstream) + pa_pstream_free(c->pstream); + c->pstream = NULL; + + if (c->client) + pa_socket_client_free(c->client); + c->client = NULL; + + for (s = c->first_stream; s; s = s->next) + stream_dead(s); + + if (c->state == CONTEXT_READY) { + c->state = CONTEXT_DEAD; + if (c->die_callback) + c->die_callback(c, c->die_userdata); + } else + c->state = CONTEXT_DEAD; +} + +static void pstream_die_callback(struct pa_pstream *p, void *userdata) { + struct pa_context *c = userdata; + assert(p && c); + c->error = PA_ERROR_CONNECTIONTERMINATED; + context_dead(c); +} + +static void pstream_packet_callback(struct pa_pstream *p, struct pa_packet *packet, void *userdata) { + struct pa_context *c = userdata; + assert(p && packet && c); + + if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) { + fprintf(stderr, "polyp.c: invalid packet.\n"); + c->error = PA_ERROR_PROTOCOL; + context_dead(c); + } +} + +static void pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata) { + struct pa_context *c = userdata; + struct pa_stream *s; + assert(p && chunk && c && chunk->memblock && chunk->memblock->data); + + if (!(s = pa_dynarray_get(c->record_streams, channel))) + return; + + if (s->read_callback) + s->read_callback(s, chunk->memblock->data + chunk->index, chunk->length, s->read_userdata); +} + +static int handle_error(struct pa_context *c, uint32_t command, struct pa_tagstruct *t) { + assert(c && t); + + if (command == PA_COMMAND_ERROR) { + if (pa_tagstruct_getu32(t, &c->error) < 0) { + c->error = PA_ERROR_PROTOCOL; + return -1; + } + + return 0; + } + + c->error = (command == PA_COMMAND_TIMEOUT) ? PA_ERROR_TIMEOUT : PA_ERROR_INTERNAL; + return -1; +} + +static void setup_complete_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct pa_context *c = userdata; + assert(pd && c && (c->state == CONTEXT_AUTHORIZING || c->state == CONTEXT_SETTING_NAME)); + + if (command != PA_COMMAND_REPLY) { + handle_error(c, command, t); + context_dead(c); + + if (c->connect_complete_callback) + c->connect_complete_callback(c, 0, c->connect_complete_userdata); + + return; + } + + if (c->state == CONTEXT_AUTHORIZING) { + struct pa_tagstruct *t; + c->state = CONTEXT_SETTING_NAME; + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_SET_NAME); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_puts(t, c->name); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c); + } else { + assert(c->state == CONTEXT_SETTING_NAME); + + c->state = CONTEXT_READY; + + if (c->connect_complete_callback) + c->connect_complete_callback(c, 1, c->connect_complete_userdata); + } + + return; +} + +static void on_connection(struct pa_socket_client *client, struct pa_iochannel*io, void *userdata) { + struct pa_context *c = userdata; + struct pa_tagstruct *t; + uint32_t tag; + assert(client && c && c->state == CONTEXT_CONNECTING); + + pa_socket_client_free(client); + c->client = NULL; + + if (!io) { + c->error = PA_ERROR_CONNECTIONREFUSED; + context_dead(c); + + if (c->connect_complete_callback) + c->connect_complete_callback(c, 0, c->connect_complete_userdata); + + return; + } + + c->pstream = pa_pstream_new(c->mainloop, io); + assert(c->pstream); + pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c); + pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c); + pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c); + + c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX); + assert(c->pdispatch); + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_AUTH); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_tagstruct_put_arbitrary(t, c->auth_cookie, sizeof(c->auth_cookie)); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c); + c->state = CONTEXT_AUTHORIZING; +} + +static struct sockaddr *resolve_server(const char *server, size_t *len) { + struct sockaddr *sa; + struct addrinfo hints, *result = NULL; + char *port; + assert(server && len); + + if ((port = strrchr(server, ':'))) + port++; + if (!port) + port = DEFAULT_PORT; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + if (getaddrinfo(server, port, &hints, &result) != 0) + return NULL; + assert(result); + + sa = malloc(*len = result->ai_addrlen); + assert(sa); + memcpy(sa, result->ai_addr, *len); + + freeaddrinfo(result); + + return sa; + +} + +int pa_context_connect(struct pa_context *c, const char *server, void (*complete) (struct pa_context*c, int success, void *userdata), void *userdata) { + assert(c && c->state == CONTEXT_UNCONNECTED); + + if (pa_authkey_load_from_home(PA_NATIVE_COOKIE_FILE, c->auth_cookie, sizeof(c->auth_cookie)) < 0) { + c->error = PA_ERROR_AUTHKEY; + return -1; + } + + if (!server) + if (!(server = getenv("POLYP_SERVER"))) + server = DEFAULT_SERVER; + + assert(!c->client); + + if (*server == '/') { + if (!(c->client = pa_socket_client_new_unix(c->mainloop, server))) { + c->error = PA_ERROR_CONNECTIONREFUSED; + return -1; + } + } else { + struct sockaddr* sa; + size_t sa_len; + + if (!(sa = resolve_server(server, &sa_len))) { + c->error = PA_ERROR_INVALIDSERVER; + return -1; + } + + c->client = pa_socket_client_new_sockaddr(c->mainloop, sa, sa_len); + free(sa); + + if (!c->client) { + c->error = PA_ERROR_CONNECTIONREFUSED; + return -1; + } + } + + c->connect_complete_callback = complete; + c->connect_complete_userdata = userdata; + + pa_socket_client_set_callback(c->client, on_connection, c); + c->state = CONTEXT_CONNECTING; + + return 0; +} + +int pa_context_is_dead(struct pa_context *c) { + assert(c); + return c->state == CONTEXT_DEAD; +} + +int pa_context_is_ready(struct pa_context *c) { + assert(c); + return c->state == CONTEXT_READY; +} + +int pa_context_errno(struct pa_context *c) { + assert(c); + return c->error; +} + +void pa_context_set_die_callback(struct pa_context *c, void (*cb)(struct pa_context *c, void *userdata), void *userdata) { + assert(c); + c->die_callback = cb; + c->die_userdata = userdata; +} + +static void command_playback_stream_killed(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct pa_context *c = userdata; + struct pa_stream *s; + uint32_t channel; + assert(pd && (command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED) && t && c); + + if (pa_tagstruct_getu32(t, &channel) < 0 || + !pa_tagstruct_eof(t)) { + c->error = PA_ERROR_PROTOCOL; + context_dead(c); + return; + } + + if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel))) + return; + + c->error = PA_ERROR_KILLED; + stream_dead(s); +} + +static void command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct pa_stream *s; + struct pa_context *c = userdata; + uint32_t bytes, channel; + assert(pd && command == PA_COMMAND_REQUEST && t && c); + + if (pa_tagstruct_getu32(t, &channel) < 0 || + pa_tagstruct_getu32(t, &bytes) < 0 || + !pa_tagstruct_eof(t)) { + c->error = PA_ERROR_PROTOCOL; + context_dead(c); + return; + } + + if (!(s = pa_dynarray_get(c->playback_streams, channel))) + return; + + if (s->state != STREAM_READY) + return; + + s->requested_bytes += bytes; + + if (s->requested_bytes && s->write_callback) + s->write_callback(s, s->requested_bytes, s->write_userdata); +} + +static void create_stream_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct pa_stream *s = userdata; + assert(pd && s && s->state == STREAM_CREATING); + + if (command != PA_COMMAND_REPLY) { + if (handle_error(s->context, command, t) < 0) { + context_dead(s->context); + return; + } + + stream_dead(s); + if (s->create_complete_callback) + s->create_complete_callback(s, 0, s->create_complete_userdata); + + return; + } + + if (pa_tagstruct_getu32(t, &s->channel) < 0 || + pa_tagstruct_getu32(t, &s->device_index) < 0 || + !pa_tagstruct_eof(t)) { + s->context->error = PA_ERROR_PROTOCOL; + context_dead(s->context); + return; + } + + s->channel_valid = 1; + pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, s); + + s->state = STREAM_READY; + if (s->create_complete_callback) + s->create_complete_callback(s, 1, s->create_complete_userdata); +} + +static void create_stream(struct pa_stream *s, uint32_t tdev_index) { + struct pa_tagstruct *t; + uint32_t tag; + assert(s); + + s->state = STREAM_CREATING; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + + pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + pa_tagstruct_puts(t, s->name); + pa_tagstruct_put_sample_spec(t, &s->sample_spec); + pa_tagstruct_putu32(t, tdev_index); + pa_tagstruct_putu32(t, s->buffer_attr.maxlength); + if (s->direction == PA_STREAM_PLAYBACK) { + pa_tagstruct_putu32(t, s->buffer_attr.tlength); + pa_tagstruct_putu32(t, s->buffer_attr.prebuf); + pa_tagstruct_putu32(t, s->buffer_attr.minreq); + } else + pa_tagstruct_putu32(t, s->buffer_attr.fragsize); + + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, s); +} + +static void lookup_device_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct pa_stream *s = userdata; + uint32_t tdev; + assert(pd && s && s->state == STREAM_LOOKING_UP); + + if (command != PA_COMMAND_REPLY) { + if (handle_error(s->context, command, t) < 0) { + context_dead(s->context); + return; + } + + stream_dead(s); + if (s->create_complete_callback) + s->create_complete_callback(s, 0, s->create_complete_userdata); + return; + } + + if (pa_tagstruct_getu32(t, &tdev) < 0 || + !pa_tagstruct_eof(t)) { + s->context->error = PA_ERROR_PROTOCOL; + context_dead(s->context); + return; + } + + create_stream(s, tdev); +} + +static void lookup_device(struct pa_stream *s, const char *tdev) { + struct pa_tagstruct *t; + uint32_t tag; + assert(s); + + s->state = STREAM_LOOKING_UP; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + + pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_LOOKUP_SINK : PA_COMMAND_LOOKUP_SOURCE); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + pa_tagstruct_puts(t, tdev); + + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, lookup_device_callback, s); +} + +struct pa_stream* pa_stream_new( + struct pa_context *c, + enum pa_stream_direction dir, + const char *dev, + const char *name, + const struct pa_sample_spec *ss, + const struct pa_buffer_attr *attr, + void (*complete) (struct pa_stream*s, int success, void *userdata), + void *userdata) { + + struct pa_stream *s; + + assert(c && name && ss && c->state == CONTEXT_READY); + + s = malloc(sizeof(struct pa_stream)); + assert(s); + s->context = c; + + s->read_callback = NULL; + s->read_userdata = NULL; + s->write_callback = NULL; + s->write_userdata = NULL; + s->die_callback = NULL; + s->die_userdata = NULL; + s->create_complete_callback = complete; + s->create_complete_userdata = NULL; + s->get_latency_callback = NULL; + s->get_latency_userdata = NULL; + + s->name = strdup(name); + s->state = STREAM_CREATING; + s->requested_bytes = 0; + s->channel = 0; + s->channel_valid = 0; + s->device_index = (uint32_t) -1; + s->direction = dir; + s->sample_spec = *ss; + if (attr) + s->buffer_attr = *attr; + else { + s->buffer_attr.maxlength = DEFAULT_MAXLENGTH; + s->buffer_attr.tlength = DEFAULT_TLENGTH; + s->buffer_attr.prebuf = DEFAULT_PREBUF; + s->buffer_attr.minreq = DEFAULT_MINREQ; + s->buffer_attr.fragsize = DEFAULT_FRAGSIZE; + } + + s->next = c->first_stream; + if (s->next) + s->next->previous = s; + s->previous = NULL; + c->first_stream = s; + + if (dev) + lookup_device(s, dev); + else + create_stream(s, (uint32_t) -1); + + return s; +} + +void pa_stream_free(struct pa_stream *s) { + assert(s && s->context); + + if (s->context->pdispatch) + pa_pdispatch_unregister_reply(s->context->pdispatch, s); + + free(s->name); + + if (s->channel_valid && s->context->state == CONTEXT_READY) { + struct pa_tagstruct *t = pa_tagstruct_new(NULL, 0); + assert(t); + + pa_tagstruct_putu32(t, PA_COMMAND_DELETE_PLAYBACK_STREAM); + pa_tagstruct_putu32(t, s->context->ctag++); + pa_tagstruct_putu32(t, s->channel); + pa_pstream_send_tagstruct(s->context->pstream, t); + } + + if (s->channel_valid) + pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL); + + if (s->next) + s->next->previous = s->previous; + if (s->previous) + s->previous->next = s->next; + else + s->context->first_stream = s->next; + + free(s); +} + +void pa_stream_set_write_callback(struct pa_stream *s, void (*cb)(struct pa_stream *p, size_t length, void *userdata), void *userdata) { + assert(s && cb); + s->write_callback = cb; + s->write_userdata = userdata; +} + +void pa_stream_write(struct pa_stream *s, const void *data, size_t length) { + struct pa_memchunk chunk; + assert(s && s->context && data && length && s->state == STREAM_READY); + + chunk.memblock = pa_memblock_new(length); + assert(chunk.memblock && chunk.memblock->data); + memcpy(chunk.memblock->data, data, length); + chunk.index = 0; + chunk.length = length; + + pa_pstream_send_memblock(s->context->pstream, s->channel, 0, &chunk); + pa_memblock_unref(chunk.memblock); + + /*fprintf(stderr, "Sent %u bytes\n", length);*/ + + if (length < s->requested_bytes) + s->requested_bytes -= length; + else + s->requested_bytes = 0; +} + +size_t pa_stream_writable_size(struct pa_stream *s) { + assert(s && s->state == STREAM_READY); + return s->requested_bytes; +} + +void pa_stream_set_read_callback(struct pa_stream *s, void (*cb)(struct pa_stream *p, const void*data, size_t length, void *userdata), void *userdata) { + assert(s && cb); + s->read_callback = cb; + s->read_userdata = userdata; +} + +int pa_stream_is_dead(struct pa_stream *s) { + return s->state == STREAM_DEAD; +} + +int pa_stream_is_ready(struct pa_stream*s) { + return s->state == STREAM_READY; +} + +void pa_stream_set_die_callback(struct pa_stream *s, void (*cb)(struct pa_stream *s, void *userdata), void *userdata) { + assert(s); + s->die_callback = cb; + s->die_userdata = userdata; +} + +int pa_context_is_pending(struct pa_context *c) { + assert(c); + + if (c->state != CONTEXT_READY) + return 0; + + return pa_pstream_is_pending(c->pstream) || pa_pdispatch_is_pending(c->pdispatch); +} + +struct pa_context* pa_stream_get_context(struct pa_stream *p) { + assert(p); + return p->context; +} + +static void set_dispatch_callbacks(struct pa_context *c); + +static void pdispatch_drain_callback(struct pa_pdispatch*pd, void *userdata) { + set_dispatch_callbacks(userdata); +} + +static void pstream_drain_callback(struct pa_pstream *s, void *userdata) { + set_dispatch_callbacks(userdata); +} + +static void set_dispatch_callbacks(struct pa_context *c) { + assert(c && c->state == CONTEXT_READY); + + pa_pstream_set_drain_callback(c->pstream, NULL, NULL); + pa_pdispatch_set_drain_callback(c->pdispatch, NULL, NULL); + + if (pa_pdispatch_is_pending(c->pdispatch)) { + pa_pdispatch_set_drain_callback(c->pdispatch, pdispatch_drain_callback, c); + return; + } + + if (pa_pstream_is_pending(c->pstream)) { + pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c); + return; + } + + assert(c->drain_complete_callback); + c->drain_complete_callback(c, c->drain_complete_userdata); +} + +int pa_context_drain( + struct pa_context *c, + void (*complete) (struct pa_context*c, void *userdata), + void *userdata) { + + assert(c && c->state == CONTEXT_READY); + + if (complete == NULL) { + c->drain_complete_callback = NULL; + pa_pstream_set_drain_callback(c->pstream, NULL, NULL); + pa_pdispatch_set_drain_callback(c->pdispatch, NULL, NULL); + return 0; + } + + if (!pa_context_is_pending(c)) + return -1; + + c->drain_complete_callback = complete; + c->drain_complete_userdata = userdata; + + set_dispatch_callbacks(c); + + return 0; +} + +static void stream_drain_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct pa_stream *s = userdata; + assert(pd && s); + + if (command != PA_COMMAND_REPLY) { + if (handle_error(s->context, command, t) < 0) { + context_dead(s->context); + return; + } + + stream_dead(s); + return; + } + + if (s->state != STREAM_READY) + return; + + if (!pa_tagstruct_eof(t)) { + s->context->error = PA_ERROR_PROTOCOL; + context_dead(s->context); + return; + } + + if (s->drain_complete_callback) { + void (*temp) (struct pa_stream*s, void *userdata) = s->drain_complete_callback; + s->drain_complete_callback = NULL; + temp(s, s->drain_complete_userdata); + } +} + + +void pa_stream_drain(struct pa_stream *s, void (*complete) (struct pa_stream*s, void *userdata), void *userdata) { + struct pa_tagstruct *t; + uint32_t tag; + assert(s && s->state == STREAM_READY); + + if (!complete) { + s->drain_complete_callback = NULL; + return; + } + + s->drain_complete_callback = complete; + s->drain_complete_userdata = userdata; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + + pa_tagstruct_putu32(t, PA_COMMAND_DRAIN_PLAYBACK_STREAM); + pa_tagstruct_putu32(t, tag = s->context->ctag++); + pa_tagstruct_putu32(t, s->channel); + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_drain_callback, s); +} + +void pa_context_exit(struct pa_context *c) { + struct pa_tagstruct *t; + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_EXIT); + pa_tagstruct_putu32(t, c->ctag++); + pa_pstream_send_tagstruct(c->pstream, t); +} + +static void context_stat_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct pa_context *c = userdata; + uint32_t total, count; + assert(pd && c); + + if (command != PA_COMMAND_REPLY) { + if (handle_error(c, command, t) < 0) { + context_dead(c); + return; + } + + if (c->stat_callback) + c->stat_callback(c, (uint32_t) -1, (uint32_t) -1, c->stat_userdata); + return; + } + + if (pa_tagstruct_getu32(t, &count) < 0 || + pa_tagstruct_getu32(t, &total) < 0 || + !pa_tagstruct_eof(t)) { + c->error = PA_ERROR_PROTOCOL; + context_dead(c); + return; + } + + if (c->stat_callback) + c->stat_callback(c, count, total, c->stat_userdata); +} + +void pa_context_stat(struct pa_context *c, void (*cb)(struct pa_context *c, uint32_t count, uint32_t total, void *userdata), void *userdata) { + uint32_t tag; + struct pa_tagstruct *t; + + c->stat_callback = cb; + c->stat_userdata = userdata; + + if (cb == NULL) + return; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_STAT); + pa_tagstruct_putu32(t, tag = c->ctag++); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_stat_callback, c); +} + +static void stream_get_latency_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct pa_stream *s = userdata; + uint32_t latency; + assert(pd && s); + + if (command != PA_COMMAND_REPLY) { + if (handle_error(s->context, command, t) < 0) { + context_dead(s->context); + return; + } + + if (s->get_latency_callback) + s->get_latency_callback(s, (uint32_t) -1, s->get_latency_userdata); + return; + } + + if (pa_tagstruct_getu32(t, &latency) < 0 || + !pa_tagstruct_eof(t)) { + s->context->error = PA_ERROR_PROTOCOL; + context_dead(s->context); + return; + } + + if (s->get_latency_callback) + s->get_latency_callback(s, latency, s->get_latency_userdata); +} + +void pa_stream_get_latency(struct pa_stream *p, void (*cb)(struct pa_stream *p, uint32_t latency, void *userdata), void *userdata) { + uint32_t tag; + struct pa_tagstruct *t; + + p->get_latency_callback = cb; + p->get_latency_userdata = userdata; + + if (cb == NULL) + return; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY); + pa_tagstruct_putu32(t, tag = p->context->ctag++); + pa_tagstruct_putu32(t, p->channel); + pa_pstream_send_tagstruct(p->context->pstream, t); + pa_pdispatch_register_reply(p->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, p); +} diff --git a/polyp/polyplib.h b/polyp/polyplib.h new file mode 100644 index 00000000..440b9658 --- /dev/null +++ b/polyp/polyplib.h @@ -0,0 +1,94 @@ +#ifndef foopolyplibhfoo +#define foopolyplibhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "sample.h" +#include "polyplib-def.h" +#include "mainloop-api.h" + +struct pa_context; + +struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *name); + +int pa_context_connect( + struct pa_context *c, + const char *server, + void (*complete) (struct pa_context*c, int success, void *userdata), + void *userdata); + +int pa_context_drain( + struct pa_context *c, + void (*complete) (struct pa_context*c, void *userdata), + void *userdata); + +void pa_context_free(struct pa_context *c); + +void pa_context_set_die_callback(struct pa_context *c, void (*cb)(struct pa_context *c, void *userdata), void *userdata); + +int pa_context_is_dead(struct pa_context *c); +int pa_context_is_ready(struct pa_context *c); +int pa_context_errno(struct pa_context *c); + +int pa_context_is_pending(struct pa_context *c); + +void pa_context_exit(struct pa_context *c); +void pa_context_stat(struct pa_context *c, void (*cb)(struct pa_context *c, uint32_t count, uint32_t total, void *userdata), void *userdata); + +struct pa_stream; + +struct pa_stream* pa_stream_new( + struct pa_context *c, + enum pa_stream_direction dir, + const char *dev, + const char *name, + const struct pa_sample_spec *ss, + const struct pa_buffer_attr *attr, + void (*complete) (struct pa_stream*s, int success, void *userdata), + void *userdata); + +void pa_stream_free(struct pa_stream *p); + +void pa_stream_drain( + struct pa_stream *s, + void (*complete) (struct pa_stream*s, void *userdata), + void *userdata); + + +void pa_stream_set_die_callback(struct pa_stream *s, void (*cb)(struct pa_stream *s, void *userdata), void *userdata); + +void pa_stream_set_write_callback(struct pa_stream *p, void (*cb)(struct pa_stream *p, size_t length, void *userdata), void *userdata); +void pa_stream_write(struct pa_stream *p, const void *data, size_t length); +size_t pa_stream_writable_size(struct pa_stream *p); + +void pa_stream_set_read_callback(struct pa_stream *p, void (*cb)(struct pa_stream *p, const void*data, size_t length, void *userdata), void *userdata); + +int pa_stream_is_dead(struct pa_stream *p); +int pa_stream_is_ready(struct pa_stream*p); + +void pa_stream_get_latency(struct pa_stream *p, void (*cb)(struct pa_stream *p, uint32_t latency, void *userdata), void *userdata); + +struct pa_context* pa_stream_get_context(struct pa_stream *p); + +#endif diff --git a/polyp/protocol-cli.c b/polyp/protocol-cli.c new file mode 100644 index 00000000..d6e69b54 --- /dev/null +++ b/polyp/protocol-cli.c @@ -0,0 +1,85 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "protocol-cli.h" +#include "cli.h" + +struct pa_protocol_cli { + struct pa_module *module; + struct pa_core *core; + struct pa_socket_server*server; + struct pa_idxset *connections; +}; + +static void cli_eof_cb(struct pa_cli*c, void*userdata) { + struct pa_protocol_cli *p = userdata; + assert(p); + pa_idxset_remove_by_data(p->connections, c, NULL); + pa_cli_free(c); +} + +static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) { + struct pa_protocol_cli *p = userdata; + struct pa_cli *c; + assert(s && io && p); + + c = pa_cli_new(p->core, io, p->module); + assert(c); + pa_cli_set_eof_callback(c, cli_eof_cb, p); + + pa_idxset_put(p->connections, c, NULL); +} + +struct pa_protocol_cli* pa_protocol_cli_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) { + struct pa_protocol_cli* p; + assert(core && server); + + p = malloc(sizeof(struct pa_protocol_cli)); + assert(p); + p->module = m; + p->core = core; + p->server = server; + p->connections = pa_idxset_new(NULL, NULL); + + pa_socket_server_set_callback(p->server, on_connection, p); + + return p; +} + +static void free_connection(void *p, void *userdata) { + assert(p); + pa_cli_free(p); +} + +void pa_protocol_cli_free(struct pa_protocol_cli *p) { + assert(p); + + pa_idxset_free(p->connections, free_connection, NULL); + pa_socket_server_free(p->server); + free(p); +} diff --git a/polyp/protocol-cli.h b/polyp/protocol-cli.h new file mode 100644 index 00000000..7ad2db75 --- /dev/null +++ b/polyp/protocol-cli.h @@ -0,0 +1,35 @@ +#ifndef fooprotocolclihfoo +#define fooprotocolclihfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "core.h" +#include "socket-server.h" +#include "module.h" +#include "modargs.h" + +struct pa_protocol_cli; + +struct pa_protocol_cli* pa_protocol_cli_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma); +void pa_protocol_cli_free(struct pa_protocol_cli *n); + +#endif diff --git a/polyp/protocol-esound.c b/polyp/protocol-esound.c new file mode 100644 index 00000000..8a7c4bcb --- /dev/null +++ b/polyp/protocol-esound.c @@ -0,0 +1,847 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#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 "authkey.h" + +#define DEFAULT_COOKIE_FILE ".esd_auth" + +#define PLAYBACK_BUFFER_SECONDS (.5) +#define PLAYBACK_BUFFER_FRAGMENTS (10) +#define RECORD_BUFFER_SECONDS (5) +#define RECORD_BUFFER_FRAGMENTS (100) + +/* This is heavily based on esound's code */ + +struct connection { + uint32_t index; + struct pa_protocol_esound *protocol; + struct pa_iochannel *io; + struct pa_client *client; + int authorized, swap_byte_order; + void *write_data; + size_t write_data_alloc, write_data_index, write_data_length; + void *read_data; + size_t read_data_alloc, read_data_length; + esd_proto_t request; + esd_client_state_t state; + struct pa_sink_input *sink_input; + struct pa_source_output *source_output; + struct pa_memblockq *input_memblockq, *output_memblockq; + void *fixed_source; + struct { + struct pa_memblock *current_memblock; + size_t memblock_index, fragment_size; + } playback; +}; + +struct pa_protocol_esound { + int public; + struct pa_module *module; + struct pa_core *core; + struct pa_socket_server *server; + struct pa_idxset *connections; + uint32_t sink_index, source_index; + unsigned n_player; + uint8_t esd_key[ESD_KEY_LEN]; +}; + +typedef struct proto_handler { + size_t data_length; + int (*proc)(struct connection *c, esd_proto_t request, const void *data, size_t length); + const char *description; +} esd_proto_handler_info_t; + +static void sink_input_drop_cb(struct pa_sink_input *i, size_t length); +static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk); +static void sink_input_kill_cb(struct pa_sink_input *i); +static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i); + +static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk); +static void source_output_kill_cb(struct pa_source_output *o); + +static int esd_proto_connect(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_stream_play(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_get_latency(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_server_info(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length); +static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const void *data, size_t length); + +/* the big map of protocol handler info */ +static struct proto_handler proto_map[ESD_PROTO_MAX] = { + { ESD_KEY_LEN + sizeof(int), esd_proto_connect, "connect" }, + { ESD_KEY_LEN + sizeof(int), NULL, "lock" }, + { ESD_KEY_LEN + sizeof(int), NULL, "unlock" }, + + { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_play, "stream play" }, + { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream rec" }, + { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream mon" }, + + { ESD_NAME_MAX + 3 * sizeof(int), NULL, "sample cache" }, + { sizeof(int), NULL, "sample free" }, + { sizeof(int), NULL, "sample play" }, + { sizeof(int), NULL, "sample loop" }, + { sizeof(int), NULL, "sample stop" }, + { -1, NULL, "TODO: sample kill" }, + + { ESD_KEY_LEN + sizeof(int), NULL, "standby" }, + { ESD_KEY_LEN + sizeof(int), NULL, "resume" }, + + { ESD_NAME_MAX, NULL, "sample getid" }, + { ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" }, + + { sizeof(int), esd_proto_server_info, "server info" }, + { sizeof(int), esd_proto_all_info, "all info" }, + { -1, NULL, "TODO: subscribe" }, + { -1, NULL, "TODO: unsubscribe" }, + + { 3 * sizeof(int), esd_proto_stream_pan, "stream pan"}, + { 3 * sizeof(int), NULL, "sample pan" }, + + { sizeof(int), NULL, "standby mode" }, + { 0, esd_proto_get_latency, "get latency" } +}; + + +static void connection_free(struct connection *c) { + assert(c); + pa_idxset_remove_by_data(c->protocol->connections, c, NULL); + + if (c->state == ESD_STREAMING_DATA) + c->protocol->n_player--; + + pa_client_free(c->client); + + if (c->sink_input) + pa_sink_input_free(c->sink_input); + if (c->source_output) + pa_source_output_free(c->source_output); + if (c->input_memblockq) + pa_memblockq_free(c->input_memblockq); + if (c->output_memblockq) + pa_memblockq_free(c->output_memblockq); + + if (c->playback.current_memblock) + pa_memblock_unref(c->playback.current_memblock); + + free(c->read_data); + free(c->write_data); + + pa_iochannel_free(c->io); + + if (c->fixed_source) + c->protocol->core->mainloop->cancel_fixed(c->protocol->core->mainloop, c->fixed_source); + + free(c); +} + +static struct pa_sink* get_output_sink(struct pa_protocol_esound *p) { + struct pa_sink *s; + assert(p); + + if (!(s = pa_idxset_get_by_index(p->core->sinks, p->sink_index))) + s = pa_sink_get_default(p->core); + + p->sink_index = s ? s->index : PA_IDXSET_INVALID; + return s; +} + +static struct pa_source* get_input_source(struct pa_protocol_esound *p) { + struct pa_source *s; + assert(p); + + if (!(s = pa_idxset_get_by_index(p->core->sources, p->sink_index))) + s = pa_source_get_default(p->core); + + p->source_index = s ? s->index : PA_IDXSET_INVALID; + return s; +} + +static void* connection_write(struct connection *c, size_t length) { + size_t t, i; + assert(c); + + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed); + c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1); + + t = c->write_data_length+length; + + if (c->write_data_alloc < t) + c->write_data = realloc(c->write_data, c->write_data_alloc = t); + + assert(c->write_data); + + i = c->write_data_length; + c->write_data_length += length; + + return c->write_data+i; +} + +/*** esound commands ***/ + +static int esd_proto_connect(struct connection *c, esd_proto_t request, const void *data, size_t length) { + uint32_t ekey; + int *ok; + assert(length == (ESD_KEY_LEN + sizeof(uint32_t))); + + if (!c->authorized) { + if (memcmp(data, c->protocol->esd_key, ESD_KEY_LEN) != 0) { + fprintf(stderr, __FILE__": kicked client with invalid authorization key.\n"); + return -1; + } + + c->authorized = 1; + } + + ekey = *(uint32_t*)(data+ESD_KEY_LEN); + if (ekey == ESD_ENDIAN_KEY) + c->swap_byte_order = 0; + else if (ekey == ESD_SWAP_ENDIAN_KEY) + c->swap_byte_order = 1; + else { + fprintf(stderr, __FILE__": client sent invalid endian key\n"); + return -1; + } + + ok = connection_write(c, sizeof(int)); + assert(ok); + *ok = 1; + return 0; +} + +static int esd_proto_stream_play(struct connection *c, esd_proto_t request, const void *data, size_t length) { + char name[ESD_NAME_MAX]; + int format, rate; + struct pa_sink *sink; + struct pa_sample_spec ss; + size_t l; + assert(c && length == (sizeof(int)*2+ESD_NAME_MAX)); + + format = maybe_swap_endian_32(c->swap_byte_order, *(int*)data); + rate = maybe_swap_endian_32(c->swap_byte_order, *((int*)data + 1)); + + ss.rate = rate; + ss.channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1; + ss.format = ((format & ESD_MASK_BITS) == ESD_BITS16) ? PA_SAMPLE_S16NE : PA_SAMPLE_U8; + + if (!pa_sample_spec_valid(&ss)) + return -1; + + if (!(sink = get_output_sink(c->protocol))) + return -1; + + strncpy(name, data + sizeof(int)*2, sizeof(name)); + name[sizeof(name)-1] = 0; + + pa_client_rename(c->client, name); + + assert(!c->input_memblockq); + + l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS); + c->input_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&ss), l/2, l/PLAYBACK_BUFFER_FRAGMENTS); + assert(c->input_memblockq); + pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2); + c->playback.fragment_size = l/10; + + assert(!c->sink_input); + c->sink_input = pa_sink_input_new(sink, name, &ss); + assert(c->sink_input); + + c->sink_input->owner = c->protocol->module; + c->sink_input->client = c->client; + c->sink_input->peek = sink_input_peek_cb; + c->sink_input->drop = sink_input_drop_cb; + c->sink_input->kill = sink_input_kill_cb; + c->sink_input->get_latency = sink_input_get_latency_cb; + c->sink_input->userdata = c; + + c->state = ESD_STREAMING_DATA; + + c->protocol->n_player++; + + return 0; +} + +static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length) { + char name[ESD_NAME_MAX]; + int format, rate; + struct pa_source *source; + struct pa_sample_spec ss; + size_t l; + assert(c && length == (sizeof(int)*2+ESD_NAME_MAX)); + + format = maybe_swap_endian_32(c->swap_byte_order, *(int*)data); + rate = maybe_swap_endian_32(c->swap_byte_order, *((int*)data + 1)); + + ss.rate = rate; + ss.channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1; + ss.format = ((format & ESD_MASK_BITS) == ESD_BITS16) ? PA_SAMPLE_S16NE : PA_SAMPLE_U8; + + if (!pa_sample_spec_valid(&ss)) + return -1; + + if (request == ESD_PROTO_STREAM_MON) { + struct pa_sink* sink; + + if (!(sink = get_output_sink(c->protocol))) + return -1; + + if (!(source = sink->monitor_source)) + return -1; + } else { + assert(request == ESD_PROTO_STREAM_REC); + + if (!(source = get_input_source(c->protocol))) + return -1; + } + + strncpy(name, data + sizeof(int)*2, sizeof(name)); + name[sizeof(name)-1] = 0; + + pa_client_rename(c->client, name); + + assert(!c->output_memblockq); + + l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS); + c->output_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&ss), 0, 0); + assert(c->output_memblockq); + pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2); + + assert(!c->source_output); + c->source_output = pa_source_output_new(source, name, &ss); + assert(c->source_output); + + c->source_output->owner = c->protocol->module; + c->source_output->client = c->client; + c->source_output->push = source_output_push_cb; + c->source_output->kill = source_output_kill_cb; + c->source_output->userdata = c; + + c->state = ESD_STREAMING_DATA; + + c->protocol->n_player++; + + return 0; +} + +static int esd_proto_get_latency(struct connection *c, esd_proto_t request, const void *data, size_t length) { + struct pa_sink *sink; + int latency, *lag; + assert(c && !data && length == 0); + + if (!(sink = get_output_sink(c->protocol))) + latency = 0; + else { + float usec = pa_sink_get_latency(sink); + usec += PLAYBACK_BUFFER_SECONDS*1000000; /* A better estimation would be a good idea! */ + latency = (int) ((usec*44100)/1000000); + } + + lag = connection_write(c, sizeof(int)); + assert(lag); + *lag = c->swap_byte_order ? swap_endian_32(latency) : latency; + return 0; +} + +static int esd_proto_server_info(struct connection *c, esd_proto_t request, const void *data, size_t length) { + int rate = 44100, format = ESD_STEREO|ESD_BITS16; + int *response; + struct pa_sink *sink; + assert(c && data && length == sizeof(int)); + + if ((sink = get_output_sink(c->protocol))) { + rate = sink->sample_spec.rate; + format = (sink->sample_spec.format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16; + format |= (sink->sample_spec.channels >= 2) ? ESD_STEREO : ESD_MONO; + } + + response = connection_write(c, sizeof(int)*3); + assert(response); + *(response++) = 0; + *(response++) = maybe_swap_endian_32(c->swap_byte_order, rate); + *(response++) = maybe_swap_endian_32(c->swap_byte_order, format); + return 0; +} + +static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length) { + void *response; + size_t t, k, s; + struct connection *conn; + size_t index = PA_IDXSET_INVALID; + assert(c && data && length == sizeof(int)); + + if (esd_proto_server_info(c, request, data, length) < 0) + return -1; + + k = sizeof(int)*5+ESD_NAME_MAX; + s = sizeof(int)*6+ESD_NAME_MAX; + response = connection_write(c, (t = s+k*(c->protocol->n_player+1))); + assert(k); + + for (conn = pa_idxset_first(c->protocol->connections, &index); conn; conn = pa_idxset_next(c->protocol->connections, &index)) { + int format = ESD_BITS16 | ESD_STEREO, rate = 44100, volume = 0xFF; + + if (conn->state != ESD_STREAMING_DATA) + continue; + + assert(t >= s+k+k); + + if (conn->sink_input) { + rate = conn->sink_input->sample_spec.rate; + volume = (conn->sink_input->volume*0xFF)/0x100; + format = (conn->sink_input->sample_spec.format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16; + format |= (conn->sink_input->sample_spec.channels >= 2) ? ESD_STEREO : ESD_MONO; + } + + /* id */ + *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, (int) conn->index); + response += sizeof(int); + + /* name */ + assert(conn->client); + strncpy(response, conn->client->name, ESD_NAME_MAX); + response += ESD_NAME_MAX; + + /* rate */ + *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, rate); + response += sizeof(int); + + /* left */ + *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, volume); + response += sizeof(int); + + /*right*/ + *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, volume); + response += sizeof(int); + + /*format*/ + *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, format); + response += sizeof(int); + + t-= k; + } + + assert(t == s+k); + memset(response, 0, t); + return 0; +} + +static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const void *data, size_t length) { + int *ok; + uint32_t index, volume; + struct connection *conn; + assert(c && data && length == sizeof(int)*3); + + index = (uint32_t) maybe_swap_endian_32(c->swap_byte_order, *(int*)data); + volume = (uint32_t) maybe_swap_endian_32(c->swap_byte_order, *((int*)data + 1)); + volume = (volume*0x100)/0xFF; + + ok = connection_write(c, sizeof(int)); + assert(ok); + + if ((conn = pa_idxset_get_by_index(c->protocol->connections, index))) { + assert(conn->sink_input); + conn->sink_input->volume = volume; + *ok = 1; + } else + *ok = 0; + + return 0; +} + +/*** client callbacks ***/ + +static void client_kill_cb(struct pa_client *c) { + assert(c && c->userdata); + connection_free(c->userdata); +} + +/*** pa_iochannel callbacks ***/ + +static int do_read(struct connection *c) { + assert(c && c->io); + + if (c->state == ESD_NEXT_REQUEST) { + ssize_t r; + assert(c->read_data_length < sizeof(c->request)); + + if ((r = pa_iochannel_read(c->io, ((void*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) { + fprintf(stderr, "protocol-esound.c: read() failed: %s\n", r == 0 ? "EOF" : strerror(errno)); + return -1; + } + + if ((c->read_data_length+= r) >= sizeof(c->request)) { + struct proto_handler *handler; + + if (c->swap_byte_order) + c->request = swap_endian_32(c->request); + + if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) { + fprintf(stderr, "protocol-esound.c: recieved invalid request.\n"); + return -1; + } + + handler = proto_map+c->request; + + if (!handler->proc) { + fprintf(stderr, "protocol-sound.c: recieved unimplemented request.\n"); + return -1; + } + + if (handler->data_length == 0) { + c->read_data_length = 0; + + if (handler->proc(c, c->request, NULL, 0) < 0) + return -1; + + } else { + if (c->read_data_alloc < handler->data_length) + c->read_data = realloc(c->read_data, c->read_data_alloc = handler->data_length); + assert(c->read_data); + + c->state = ESD_NEEDS_REQDATA; + c->read_data_length = 0; + } + } + + } else if (c->state == ESD_NEEDS_REQDATA) { + ssize_t r; + struct proto_handler *handler = proto_map+c->request; + + assert(handler->proc); + + assert(c->read_data && c->read_data_length < handler->data_length); + + if ((r = pa_iochannel_read(c->io, c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) { + fprintf(stderr, "protocol-esound.c: read() failed: %s\n", r == 0 ? "EOF" : strerror(errno)); + return -1; + } + + if ((c->read_data_length+= r) >= handler->data_length) { + size_t l = c->read_data_length; + assert(handler->proc); + + c->state = ESD_NEXT_REQUEST; + c->read_data_length = 0; + + if (handler->proc(c, c->request, c->read_data, l) < 0) + return -1; + } + } else if (c->state == ESD_STREAMING_DATA && c->sink_input) { + struct pa_memchunk chunk; + ssize_t r; + size_t l; + + assert(c->input_memblockq); + + if (!(l = pa_memblockq_missing(c->input_memblockq))) + return 0; + + if (l > c->playback.fragment_size) + l = c->playback.fragment_size; + + if (c->playback.current_memblock) + if (c->playback.current_memblock->length - c->playback.memblock_index < l) { + pa_memblock_unref(c->playback.current_memblock); + c->playback.current_memblock = NULL; + c->playback.memblock_index = 0; + } + + if (!c->playback.current_memblock) { + c->playback.current_memblock = pa_memblock_new(c->playback.fragment_size*2); + assert(c->playback.current_memblock && c->playback.current_memblock->length >= l); + c->playback.memblock_index = 0; + } + + if ((r = pa_iochannel_read(c->io, c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) { + fprintf(stderr, __FILE__": read() failed: %s\n", r == 0 ? "EOF" : strerror(errno)); + return -1; + } + + chunk.memblock = c->playback.current_memblock; + chunk.index = c->playback.memblock_index; + chunk.length = r; + assert(chunk.memblock); + + c->playback.memblock_index += r; + + assert(c->input_memblockq); + pa_memblockq_push_align(c->input_memblockq, &chunk, 0); + assert(c->sink_input); + pa_sink_notify(c->sink_input->sink); + + } + + return 0; +} + +static int do_write(struct connection *c) { + assert(c && c->io); + + if (c->write_data_length) { + ssize_t r; + + assert(c->write_data_index < c->write_data_length); + if ((r = pa_iochannel_write(c->io, c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) { + fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno)); + return -1; + } + + if ((c->write_data_index +=r) >= c->write_data_length) + c->write_data_length = c->write_data_index = 0; + + } else if (c->state == ESD_STREAMING_DATA && c->source_output) { + struct pa_memchunk chunk; + ssize_t r; + + assert(c->output_memblockq); + if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) + return 0; + + assert(chunk.memblock && chunk.length); + + if ((r = pa_iochannel_write(c->io, chunk.memblock->data+chunk.index, chunk.length)) < 0) { + pa_memblock_unref(chunk.memblock); + fprintf(stderr, __FILE__": write(): %s\n", strerror(errno)); + return -1; + } + + pa_memblockq_drop(c->output_memblockq, r); + pa_memblock_unref(chunk.memblock); + } + + return 0; +} + +static void do_work(struct connection *c) { + assert(c); + + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed); + c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 0); + + if (pa_iochannel_is_hungup(c->io)) + goto fail; + + if (pa_iochannel_is_writable(c->io)) + if (do_write(c) < 0) + goto fail; + + if (pa_iochannel_is_readable(c->io)) + if (do_read(c) < 0) + goto fail; + + return; + +fail: + connection_free(c); +} + +static void io_callback(struct pa_iochannel*io, void *userdata) { + struct connection *c = userdata; + assert(io && c && c->io == io); + + do_work(c); +} + +/*** fixed callback ***/ + +static void fixed_callback(struct pa_mainloop_api*a, void *id, void *userdata) { + struct connection *c = userdata; + assert(a && c && c->fixed_source == id); + + do_work(c); +} + +/*** sink_input callbacks ***/ + +static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk) { + struct connection*c; + assert(i && i->userdata && chunk); + c = i->userdata; + + if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) + return -1; + + return 0; +} + +static void sink_input_drop_cb(struct pa_sink_input *i, size_t length) { + struct connection*c = i->userdata; + assert(i && c && length); + + pa_memblockq_drop(c->input_memblockq, length); + + /* do something */ + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed); + c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1); +} + +static void sink_input_kill_cb(struct pa_sink_input *i) { + assert(i && i->userdata); + connection_free((struct connection *) i->userdata); +} + + +static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i) { + struct connection*c = i->userdata; + assert(i && c); + return pa_samples_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); +} + +/*** source_output callbacks ***/ + +static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk) { + struct connection *c = o->userdata; + assert(o && c && chunk); + + pa_memblockq_push(c->output_memblockq, chunk, 0); + + /* do something */ + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed); + c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1); +} + +static void source_output_kill_cb(struct pa_source_output *o) { + assert(o && o->userdata); + connection_free((struct connection *) o->userdata); +} + +/*** socket server callback ***/ + +static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) { + struct connection *c; + char cname[256]; + assert(s && io && userdata); + + c = malloc(sizeof(struct connection)); + assert(c); + c->protocol = userdata; + c->io = io; + pa_iochannel_set_callback(c->io, io_callback, c); + + pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); + assert(c->protocol->core); + c->client = pa_client_new(c->protocol->core, "ESOUND", cname); + assert(c->client); + c->client->owner = c->protocol->module; + c->client->kill = client_kill_cb; + c->client->userdata = c; + + c->authorized = c->protocol->public; + c->swap_byte_order = 0; + + c->read_data_length = 0; + c->read_data = malloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length); + assert(c->read_data); + + c->write_data_length = c->write_data_index = c->write_data_alloc = 0; + c->write_data = NULL; + + c->state = ESD_NEEDS_REQDATA; + c->request = ESD_PROTO_CONNECT; + + c->sink_input = NULL; + c->input_memblockq = NULL; + + c->source_output = NULL; + c->output_memblockq = NULL; + + c->playback.current_memblock = NULL; + c->playback.memblock_index = 0; + c->playback.fragment_size = 0; + + c->fixed_source = c->protocol->core->mainloop->source_fixed(c->protocol->core->mainloop, fixed_callback, c); + assert(c->fixed_source); + c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 0); + + pa_idxset_put(c->protocol->connections, c, &c->index); +} + +/*** entry points ***/ + +struct pa_protocol_esound* pa_protocol_esound_new(struct pa_core*core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) { + uint32_t source_index, sink_index; + struct pa_protocol_esound *p; + assert(core && server && ma); + + if (pa_modargs_get_source_index(ma, core, &source_index) < 0) { + fprintf(stderr, __FILE__": source does not exist.\n"); + return NULL; + } + + if (pa_modargs_get_sink_index(ma, core, &sink_index) < 0) { + fprintf(stderr, __FILE__": sink does not exist.\n"); + return NULL; + } + p = malloc(sizeof(struct pa_protocol_esound)); + assert(p); + + if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", DEFAULT_COOKIE_FILE), p->esd_key, sizeof(p->esd_key)) < 0) { + free(p); + return NULL; + } + + p->module = m; + p->public = 0; + p->server = server; + pa_socket_server_set_callback(p->server, on_connection, p); + p->core = core; + p->connections = pa_idxset_new(NULL, NULL); + assert(p->connections); + p->sink_index = sink_index; + p->source_index = source_index; + p->n_player = 0; + + return p; +} + +void pa_protocol_esound_free(struct pa_protocol_esound *p) { + struct connection *c; + assert(p); + + while ((c = pa_idxset_first(p->connections, NULL))) + connection_free(c); + + pa_idxset_free(p->connections, NULL, NULL); + pa_socket_server_free(p->server); + free(p); +} diff --git a/polyp/protocol-esound.h b/polyp/protocol-esound.h new file mode 100644 index 00000000..b2bdd31b --- /dev/null +++ b/polyp/protocol-esound.h @@ -0,0 +1,35 @@ +#ifndef fooprotocolesoundhfoo +#define fooprotocolesoundhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "core.h" +#include "socket-server.h" +#include "module.h" +#include "modargs.h" + +struct pa_protocol_esound; + +struct pa_protocol_esound* pa_protocol_esound_new(struct pa_core*core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma); +void pa_protocol_esound_free(struct pa_protocol_esound *p); + +#endif diff --git a/polyp/protocol-native.c b/polyp/protocol-native.c new file mode 100644 index 00000000..83c910d1 --- /dev/null +++ b/polyp/protocol-native.c @@ -0,0 +1,863 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#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" + +struct connection; +struct pa_protocol_native; + +struct record_stream { + struct connection *connection; + uint32_t index; + struct pa_source_output *source_output; + struct pa_memblockq *memblockq; + size_t fragment_size; +}; + +struct playback_stream { + struct connection *connection; + uint32_t index; + struct pa_sink_input *sink_input; + struct pa_memblockq *memblockq; + size_t requested_bytes; + int drain_request; + uint32_t drain_tag; +}; + +struct connection { + int authorized; + struct pa_protocol_native *protocol; + struct pa_client *client; + struct pa_pstream *pstream; + struct pa_pdispatch *pdispatch; + struct pa_idxset *record_streams, *playback_streams; + uint32_t rrobin_index; +}; + +struct pa_protocol_native { + struct pa_module *module; + int public; + struct pa_core *core; + struct pa_socket_server *server; + struct pa_idxset *connections; + uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH]; +}; + +static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk); +static void sink_input_drop_cb(struct pa_sink_input *i, size_t length); +static void sink_input_kill_cb(struct pa_sink_input *i); +static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i); + +static void request_bytes(struct playback_stream*s); + +static void source_output_kill_cb(struct pa_source_output *o); +static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk); + +static void command_exit(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_delete_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_drain_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_delete_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_auth(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_set_name(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_lookup(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_stat(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); +static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); + +static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = { + [PA_COMMAND_ERROR] = { NULL }, + [PA_COMMAND_TIMEOUT] = { NULL }, + [PA_COMMAND_REPLY] = { NULL }, + [PA_COMMAND_CREATE_PLAYBACK_STREAM] = { command_create_playback_stream }, + [PA_COMMAND_DELETE_PLAYBACK_STREAM] = { command_delete_playback_stream }, + [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = { command_drain_playback_stream }, + [PA_COMMAND_CREATE_RECORD_STREAM] = { command_create_record_stream }, + [PA_COMMAND_DELETE_RECORD_STREAM] = { command_delete_record_stream }, + [PA_COMMAND_AUTH] = { command_auth }, + [PA_COMMAND_REQUEST] = { NULL }, + [PA_COMMAND_EXIT] = { command_exit }, + [PA_COMMAND_SET_NAME] = { command_set_name }, + [PA_COMMAND_LOOKUP_SINK] = { command_lookup }, + [PA_COMMAND_LOOKUP_SOURCE] = { command_lookup }, + [PA_COMMAND_STAT] = { command_stat }, + [PA_COMMAND_GET_PLAYBACK_LATENCY] = { command_get_playback_latency }, +}; + +/* structure management */ + +static struct record_stream* record_stream_new(struct connection *c, struct pa_source *source, struct pa_sample_spec *ss, const char *name, size_t maxlength, size_t fragment_size) { + struct record_stream *s; + struct pa_source_output *source_output; + size_t base; + assert(c && source && ss && name && maxlength); + + if (!(source_output = pa_source_output_new(source, name, ss))) + return NULL; + + s = malloc(sizeof(struct record_stream)); + assert(s); + s->connection = c; + s->source_output = source_output; + s->source_output->push = source_output_push_cb; + s->source_output->kill = source_output_kill_cb; + s->source_output->userdata = s; + s->source_output->owner = c->protocol->module; + s->source_output->client = c->client; + + s->memblockq = pa_memblockq_new(maxlength, 0, base = pa_sample_size(ss), 0, 0); + assert(s->memblockq); + + s->fragment_size = (fragment_size/base)*base; + if (!s->fragment_size) + s->fragment_size = base; + + pa_idxset_put(c->record_streams, s, &s->index); + return s; +} + +static void record_stream_free(struct record_stream* r) { + assert(r && r->connection); + + pa_idxset_remove_by_data(r->connection->record_streams, r, NULL); + pa_source_output_free(r->source_output); + pa_memblockq_free(r->memblockq); + free(r); +} + +static struct playback_stream* playback_stream_new(struct connection *c, struct pa_sink *sink, struct pa_sample_spec *ss, const char *name, + size_t maxlength, + size_t tlength, + size_t prebuf, + size_t minreq) { + struct playback_stream *s; + struct pa_sink_input *sink_input; + assert(c && sink && ss && name && maxlength); + + if (!(sink_input = pa_sink_input_new(sink, name, ss))) + return NULL; + + s = malloc(sizeof(struct playback_stream)); + assert (s); + s->connection = c; + s->sink_input = sink_input; + + s->sink_input->peek = sink_input_peek_cb; + s->sink_input->drop = sink_input_drop_cb; + s->sink_input->kill = sink_input_kill_cb; + s->sink_input->get_latency = sink_input_get_latency_cb; + s->sink_input->userdata = s; + s->sink_input->owner = c->protocol->module; + s->sink_input->client = c->client; + + s->memblockq = pa_memblockq_new(maxlength, tlength, pa_sample_size(ss), prebuf, minreq); + assert(s->memblockq); + + s->requested_bytes = 0; + s->drain_request = 0; + + pa_idxset_put(c->playback_streams, s, &s->index); + return s; +} + +static void playback_stream_free(struct playback_stream* p) { + assert(p && p->connection); + + if (p->drain_request) + pa_pstream_send_error(p->connection->pstream, p->drain_tag, PA_ERROR_NOENTITY); + + pa_idxset_remove_by_data(p->connection->playback_streams, p, NULL); + pa_sink_input_free(p->sink_input); + pa_memblockq_free(p->memblockq); + free(p); +} + +static void connection_free(struct connection *c) { + struct record_stream *r; + struct playback_stream *p; + assert(c && c->protocol); + + pa_idxset_remove_by_data(c->protocol->connections, c, NULL); + while ((r = pa_idxset_first(c->record_streams, NULL))) + record_stream_free(r); + pa_idxset_free(c->record_streams, NULL, NULL); + + while ((p = pa_idxset_first(c->playback_streams, NULL))) + playback_stream_free(p); + pa_idxset_free(c->playback_streams, NULL, NULL); + + pa_pdispatch_free(c->pdispatch); + pa_pstream_free(c->pstream); + pa_client_free(c->client); + free(c); +} + +static void request_bytes(struct playback_stream *s) { + struct pa_tagstruct *t; + size_t l; + assert(s); + + if (!(l = pa_memblockq_missing(s->memblockq))) + return; + + if (l <= s->requested_bytes) + return; + + l -= s->requested_bytes; + + if (l < pa_memblockq_get_minreq(s->memblockq)) + return; + + s->requested_bytes += l; + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_REQUEST); + pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ + pa_tagstruct_putu32(t, s->index); + pa_tagstruct_putu32(t, l); + pa_pstream_send_tagstruct(s->connection->pstream, t); + + /*fprintf(stderr, "Requesting %u bytes\n", l);*/ +} + +static void send_memblock(struct connection *c) { + uint32_t start; + struct record_stream *r; + + start = PA_IDXSET_INVALID; + for (;;) { + struct pa_memchunk chunk; + + if (!(r = pa_idxset_rrobin(c->record_streams, &c->rrobin_index))) + return; + + if (start == PA_IDXSET_INVALID) + start = c->rrobin_index; + else if (start == c->rrobin_index) + return; + + if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) { + if (chunk.length > r->fragment_size) + chunk.length = r->fragment_size; + + pa_pstream_send_memblock(c->pstream, r->index, 0, &chunk); + pa_memblockq_drop(r->memblockq, chunk.length); + pa_memblock_unref(chunk.memblock); + + return; + } + } +} + +static void send_playback_stream_killed(struct playback_stream *p) { + struct pa_tagstruct *t; + assert(p); + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_KILLED); + pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ + pa_tagstruct_putu32(t, p->index); + pa_pstream_send_tagstruct(p->connection->pstream, t); +} + +static void send_record_stream_killed(struct record_stream *r) { + struct pa_tagstruct *t; + assert(r); + + t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_KILLED); + pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ + pa_tagstruct_putu32(t, r->index); + pa_pstream_send_tagstruct(r->connection->pstream, t); +} + + +/*** sinkinput callbacks ***/ + +static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk) { + struct playback_stream *s; + assert(i && i->userdata && chunk); + s = i->userdata; + + if (pa_memblockq_peek(s->memblockq, chunk) < 0) + return -1; + + return 0; +} + +static void sink_input_drop_cb(struct pa_sink_input *i, size_t length) { + struct playback_stream *s; + assert(i && i->userdata && length); + s = i->userdata; + + pa_memblockq_drop(s->memblockq, length); + request_bytes(s); + + if (s->drain_request && !pa_memblockq_is_readable(s->memblockq)) { + pa_pstream_send_simple_ack(s->connection->pstream, s->drain_tag); + s->drain_request = 0; + } +} + +static void sink_input_kill_cb(struct pa_sink_input *i) { + assert(i && i->userdata); + send_playback_stream_killed((struct playback_stream *) i->userdata); + playback_stream_free((struct playback_stream *) i->userdata); +} + +static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i) { + struct playback_stream *s; + assert(i && i->userdata); + s = i->userdata; + + return pa_samples_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec); +} + +/*** source_output callbacks ***/ + +static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk) { + struct record_stream *s; + assert(o && o->userdata && chunk); + s = o->userdata; + + pa_memblockq_push(s->memblockq, chunk, 0); + if (!pa_pstream_is_pending(s->connection->pstream)) + send_memblock(s->connection); +} + +static void source_output_kill_cb(struct pa_source_output *o) { + assert(o && o->userdata); + send_record_stream_killed((struct record_stream *) o->userdata); + record_stream_free((struct record_stream *) o->userdata); +} + +/*** pdispatch callbacks ***/ + +static void protocol_error(struct connection *c) { + fprintf(stderr, __FILE__": protocol error, kicking client\n"); + connection_free(c); +} + +static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + struct playback_stream *s; + size_t maxlength, tlength, prebuf, minreq; + uint32_t sink_index; + const char *name; + struct pa_sample_spec ss; + struct pa_tagstruct *reply; + struct pa_sink *sink; + assert(c && t && c->protocol && c->protocol->core); + + if (pa_tagstruct_gets(t, &name) < 0 || + pa_tagstruct_get_sample_spec(t, &ss) < 0 || + pa_tagstruct_getu32(t, &sink_index) < 0 || + pa_tagstruct_getu32(t, &maxlength) < 0 || + pa_tagstruct_getu32(t, &tlength) < 0 || + pa_tagstruct_getu32(t, &prebuf) < 0 || + pa_tagstruct_getu32(t, &minreq) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (sink_index == (uint32_t) -1) + sink = pa_sink_get_default(c->protocol->core); + else + sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index); + + if (!sink) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + if (!(s = playback_stream_new(c, sink, &ss, name, maxlength, tlength, prebuf, minreq))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID); + return; + } + + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_putu32(reply, s->index); + assert(s->sink_input); + pa_tagstruct_putu32(reply, s->sink_input->index); + pa_pstream_send_tagstruct(c->pstream, reply); + request_bytes(s); +} + +static void command_delete_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t channel; + struct playback_stream *s; + assert(c && t); + + if (pa_tagstruct_getu32(t, &channel) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!(s = pa_idxset_get_by_index(c->playback_streams, channel))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); + return; + } + + playback_stream_free(s); + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + struct record_stream *s; + size_t maxlength, fragment_size; + uint32_t source_index; + const char *name; + struct pa_sample_spec ss; + struct pa_tagstruct *reply; + struct pa_source *source; + assert(c && t && c->protocol && c->protocol->core); + + if (pa_tagstruct_gets(t, &name) < 0 || + pa_tagstruct_get_sample_spec(t, &ss) < 0 || + pa_tagstruct_getu32(t, &source_index) < 0 || + pa_tagstruct_getu32(t, &maxlength) < 0 || + pa_tagstruct_getu32(t, &fragment_size) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (source_index == (uint32_t) -1) + source = pa_source_get_default(c->protocol->core); + else + source = pa_idxset_get_by_index(c->protocol->core->sources, source_index); + + if (!source) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + if (!(s = record_stream_new(c, source, &ss, name, maxlength, fragment_size))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID); + return; + } + + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_putu32(reply, s->index); + assert(s->source_output); + pa_tagstruct_putu32(reply, s->source_output->index); + pa_pstream_send_tagstruct(c->pstream, reply); +} + +static void command_delete_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t channel; + struct record_stream *s; + assert(c && t); + + if (pa_tagstruct_getu32(t, &channel) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); + return; + } + + record_stream_free(s); + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_exit(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + assert(c && t); + + if (!pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop); + c->protocol->core->mainloop->quit(c->protocol->core->mainloop, 0); + pa_pstream_send_simple_ack(c->pstream, tag); /* nonsense */ + return; +} + +static void command_auth(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + const void*cookie; + assert(c && t); + + if (pa_tagstruct_get_arbitrary(t, &cookie, PA_NATIVE_COOKIE_LENGTH) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) != 0) { + fprintf(stderr, "protocol-native.c: Denied access to client with invalid authorization key.\n"); + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + c->authorized = 1; + pa_pstream_send_simple_ack(c->pstream, tag); + return; +} + +static void command_set_name(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + const char *name; + assert(c && t); + + if (pa_tagstruct_gets(t, &name) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + pa_client_rename(c->client, name); + pa_pstream_send_simple_ack(c->pstream, tag); + return; +} + +static void command_lookup(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + const char *name; + uint32_t index = PA_IDXSET_INVALID; + assert(c && t); + + if (pa_tagstruct_gets(t, &name) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (command == PA_COMMAND_LOOKUP_SINK) { + struct pa_sink *sink; + if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK))) + index = sink->index; + } else { + struct pa_source *source; + assert(command == PA_COMMAND_LOOKUP_SOURCE); + if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE))) + index = source->index; + } + + if (index == PA_IDXSET_INVALID) + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + else { + struct pa_tagstruct *reply; + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_putu32(reply, index); + pa_pstream_send_tagstruct(c->pstream, reply); + } +} + +static void command_drain_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + uint32_t index; + struct playback_stream *s; + assert(c && t); + + if (pa_tagstruct_getu32(t, &index) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!(s = pa_idxset_get_by_index(c->playback_streams, index))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + s->drain_request = 0; + + if (!pa_memblockq_is_readable(s->memblockq)) + pa_pstream_send_simple_ack(c->pstream, tag); + else { + s->drain_request = 1; + s->drain_tag = tag; + } +} + +static void command_stat(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + assert(c && t); + struct pa_tagstruct *reply; + + if (!pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_putu32(reply, pa_memblock_get_count()); + pa_tagstruct_putu32(reply, pa_memblock_get_total()); + pa_pstream_send_tagstruct(c->pstream, reply); +} + +static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { + struct connection *c = userdata; + assert(c && t); + struct pa_tagstruct *reply; + struct playback_stream *s; + uint32_t index, latency; + + if (pa_tagstruct_getu32(t, &index) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + if (!c->authorized) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); + return; + } + + if (!(s = pa_idxset_get_by_index(c->playback_streams, index))) { + pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); + return; + } + + latency = pa_sink_input_get_latency(s->sink_input); + reply = pa_tagstruct_new(NULL, 0); + assert(reply); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + pa_tagstruct_putu32(reply, latency); + pa_pstream_send_tagstruct(c->pstream, reply); +} + +/*** pstream callbacks ***/ + +static void pstream_packet_callback(struct pa_pstream *p, struct pa_packet *packet, void *userdata) { + struct connection *c = userdata; + assert(p && packet && packet->data && c); + + if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) { + fprintf(stderr, "protocol-native: invalid packet.\n"); + connection_free(c); + } +} + +static void pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata) { + struct connection *c = userdata; + struct playback_stream *stream; + assert(p && chunk && userdata); + + if (!(stream = pa_idxset_get_by_index(c->playback_streams, channel))) { + fprintf(stderr, "protocol-native: client sent block for invalid stream.\n"); + connection_free(c); + return; + } + + if (chunk->length >= stream->requested_bytes) + stream->requested_bytes = 0; + else + stream->requested_bytes -= chunk->length; + + pa_memblockq_push_align(stream->memblockq, chunk, delta); + assert(stream->sink_input); + pa_sink_notify(stream->sink_input->sink); + + /*fprintf(stderr, "Recieved %u bytes.\n", chunk->length);*/ +} + +static void pstream_die_callback(struct pa_pstream *p, void *userdata) { + struct connection *c = userdata; + assert(p && c); + connection_free(c); + + fprintf(stderr, "protocol-native: connection died.\n"); +} + + +static void pstream_drain_callback(struct pa_pstream *p, void *userdata) { + struct connection *c = userdata; + assert(p && c); + + send_memblock(c); +} + +/*** client callbacks ***/ + +static void client_kill_cb(struct pa_client *c) { + assert(c && c->userdata); + connection_free(c->userdata); +} + +/*** socket server callbacks ***/ + +static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) { + struct pa_protocol_native *p = userdata; + struct connection *c; + assert(s && io && p); + + c = malloc(sizeof(struct connection)); + assert(c); + c->authorized = p->public; + c->protocol = p; + assert(p->core); + c->client = pa_client_new(p->core, "NATIVE", "Client"); + assert(c->client); + c->client->kill = client_kill_cb; + c->client->userdata = c; + c->client->owner = p->module; + + c->pstream = pa_pstream_new(p->core->mainloop, io); + assert(c->pstream); + + pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c); + pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c); + pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c); + pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c); + + c->pdispatch = pa_pdispatch_new(p->core->mainloop, command_table, PA_COMMAND_MAX); + assert(c->pdispatch); + + c->record_streams = pa_idxset_new(NULL, NULL); + c->playback_streams = pa_idxset_new(NULL, NULL); + assert(c->record_streams && c->playback_streams); + + c->rrobin_index = PA_IDXSET_INVALID; + + pa_idxset_put(p->connections, c, NULL); +} + +/*** module entry points ***/ + +struct pa_protocol_native* pa_protocol_native_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) { + struct pa_protocol_native *p; + uint32_t public; + assert(core && server && ma); + + if (pa_modargs_get_value_u32(ma, "public", &public) < 0) { + fprintf(stderr, __FILE__": public= expects numeric argument.\n"); + return NULL; + } + + p = malloc(sizeof(struct pa_protocol_native)); + assert(p); + + if (pa_authkey_load_from_home(pa_modargs_get_value(ma, "cookie", PA_NATIVE_COOKIE_FILE), p->auth_cookie, sizeof(p->auth_cookie)) < 0) { + free(p); + return NULL; + } + + p->module = m; + p->public = public; + p->server = server; + p->core = core; + p->connections = pa_idxset_new(NULL, NULL); + assert(p->connections); + + pa_socket_server_set_callback(p->server, on_connection, p); + + return p; +} + +void pa_protocol_native_free(struct pa_protocol_native *p) { + struct connection *c; + assert(p); + + while ((c = pa_idxset_first(p->connections, NULL))) + connection_free(c); + pa_idxset_free(p->connections, NULL, NULL); + pa_socket_server_free(p->server); + free(p); +} diff --git a/polyp/protocol-native.h b/polyp/protocol-native.h new file mode 100644 index 00000000..3d9fdde1 --- /dev/null +++ b/polyp/protocol-native.h @@ -0,0 +1,35 @@ +#ifndef fooprotocolnativehfoo +#define fooprotocolnativehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "core.h" +#include "socket-server.h" +#include "module.h" +#include "modargs.h" + +struct pa_protocol_native; + +struct pa_protocol_native* pa_protocol_native_new(struct pa_core*core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma); +void pa_protocol_native_free(struct pa_protocol_native *n); + +#endif diff --git a/polyp/protocol-simple.c b/polyp/protocol-simple.c new file mode 100644 index 00000000..3a52e311 --- /dev/null +++ b/polyp/protocol-simple.c @@ -0,0 +1,444 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#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" + +struct connection { + struct pa_protocol_simple *protocol; + struct pa_iochannel *io; + struct pa_sink_input *sink_input; + struct pa_source_output *source_output; + struct pa_client *client; + struct pa_memblockq *input_memblockq, *output_memblockq; + void *fixed_source; + + struct { + struct pa_memblock *current_memblock; + size_t memblock_index, fragment_size; + } playback; +}; + +struct pa_protocol_simple { + struct pa_module *module; + struct pa_core *core; + struct pa_socket_server*server; + struct pa_idxset *connections; + enum { + RECORD = 1, + PLAYBACK = 2, + DUPLEX = 3 + } mode; + struct pa_sample_spec sample_spec; + uint32_t sink_index, source_index; +}; + +#define PLAYBACK_BUFFER_SECONDS (.5) +#define PLAYBACK_BUFFER_FRAGMENTS (10) +#define RECORD_BUFFER_SECONDS (5) +#define RECORD_BUFFER_FRAGMENTS (100) + +static void connection_free(struct connection *c) { + assert(c); + + pa_idxset_remove_by_data(c->protocol->connections, c, NULL); + + if (c->playback.current_memblock) + pa_memblock_unref(c->playback.current_memblock); + if (c->sink_input) + pa_sink_input_free(c->sink_input); + if (c->source_output) + pa_source_output_free(c->source_output); + if (c->client) + pa_client_free(c->client); + if (c->io) + pa_iochannel_free(c->io); + if (c->input_memblockq) + pa_memblockq_free(c->input_memblockq); + if (c->output_memblockq) + pa_memblockq_free(c->output_memblockq); + if (c->fixed_source) + c->protocol->core->mainloop->cancel_fixed(c->protocol->core->mainloop, c->fixed_source); + free(c); +} + +static int do_read(struct connection *c) { + struct pa_memchunk chunk; + ssize_t r; + size_t l; + + if (!c->sink_input || !(l = pa_memblockq_missing(c->input_memblockq))) + return 0; + + if (l > c->playback.fragment_size) + l = c->playback.fragment_size; + + if (c->playback.current_memblock) + if (c->playback.current_memblock->length - c->playback.memblock_index < l) { + pa_memblock_unref(c->playback.current_memblock); + c->playback.current_memblock = NULL; + c->playback.memblock_index = 0; + } + + if (!c->playback.current_memblock) { + c->playback.current_memblock = pa_memblock_new(c->playback.fragment_size*2); + assert(c->playback.current_memblock && c->playback.current_memblock->length >= l); + c->playback.memblock_index = 0; + } + + if ((r = pa_iochannel_read(c->io, c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) { + fprintf(stderr, __FILE__": read() failed: %s\n", r == 0 ? "EOF" : strerror(errno)); + return -1; + } + + chunk.memblock = c->playback.current_memblock; + chunk.index = c->playback.memblock_index; + chunk.length = r; + assert(chunk.memblock); + + c->playback.memblock_index += r; + + assert(c->input_memblockq); + pa_memblockq_push_align(c->input_memblockq, &chunk, 0); + assert(c->sink_input); + pa_sink_notify(c->sink_input->sink); + + return 0; +} + +static int do_write(struct connection *c) { + struct pa_memchunk chunk; + ssize_t r; + + if (!c->source_output) + return 0; + + assert(c->output_memblockq); + if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) + return 0; + + assert(chunk.memblock && chunk.length); + + if ((r = pa_iochannel_write(c->io, chunk.memblock->data+chunk.index, chunk.length)) < 0) { + pa_memblock_unref(chunk.memblock); + fprintf(stderr, "write(): %s\n", strerror(errno)); + return -1; + } + + pa_memblockq_drop(c->output_memblockq, r); + pa_memblock_unref(chunk.memblock); + + return 0; +} + + +static void do_work(struct connection *c) { + assert(c); + + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed); + c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 0); + + if (pa_iochannel_is_hungup(c->io)) + goto fail; + + if (pa_iochannel_is_writable(c->io)) + if (do_write(c) < 0) + goto fail; + + if (pa_iochannel_is_readable(c->io)) + if (do_read(c) < 0) + goto fail; + + return; + +fail: + connection_free(c); +} + +/*** sink_input callbacks ***/ + +static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk) { + struct connection*c; + assert(i && i->userdata && chunk); + c = i->userdata; + + if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) + return -1; + + return 0; +} + +static void sink_input_drop_cb(struct pa_sink_input *i, size_t length) { + struct connection*c = i->userdata; + assert(i && c && length); + + pa_memblockq_drop(c->input_memblockq, length); + + /* do something */ + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed); + c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1); +} + +static void sink_input_kill_cb(struct pa_sink_input *i) { + assert(i && i->userdata); + connection_free((struct connection *) i->userdata); +} + + +static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i) { + struct connection*c = i->userdata; + assert(i && c); + return pa_samples_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); +} + +/*** source_output callbacks ***/ + +static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk) { + struct connection *c = o->userdata; + assert(o && c && chunk); + + pa_memblockq_push(c->output_memblockq, chunk, 0); + + /* do something */ + assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed); + c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1); +} + +static void source_output_kill_cb(struct pa_source_output *o) { + assert(o && o->userdata); + connection_free((struct connection *) o->userdata); +} + +/*** client callbacks ***/ + +static void client_kill_cb(struct pa_client *c) { + assert(c && c->userdata); + connection_free((struct connection *) c->userdata); +} + +/*** pa_iochannel callbacks ***/ + +static void io_callback(struct pa_iochannel*io, void *userdata) { + struct connection *c = userdata; + assert(io && c && c->io == io); + + do_work(c); +} + +/*** fixed callback ***/ + +static void fixed_callback(struct pa_mainloop_api*a, void *id, void *userdata) { + struct connection *c = userdata; + assert(a && c && c->fixed_source == id); + + do_work(c); +} + +/*** socket_server callbacks ***/ + +static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) { + struct pa_protocol_simple *p = userdata; + struct connection *c = NULL; + char cname[256]; + assert(s && io && p); + + c = malloc(sizeof(struct connection)); + assert(c); + c->io = io; + c->sink_input = NULL; + c->source_output = NULL; + c->fixed_source = NULL; + c->input_memblockq = c->output_memblockq = NULL; + c->protocol = p; + c->playback.current_memblock = NULL; + c->playback.memblock_index = 0; + c->playback.fragment_size = 0; + + pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); + c->client = pa_client_new(p->core, "SIMPLE", cname); + assert(c->client); + c->client->owner = p->module; + c->client->kill = client_kill_cb; + c->client->userdata = c; + + if (p->mode & PLAYBACK) { + struct pa_sink *sink; + size_t l; + + if (!(sink = pa_idxset_get_by_index(p->core->sinks, p->sink_index))) + if (!(sink = pa_sink_get_default(p->core))) { + fprintf(stderr, "Failed to get sink.\n"); + goto fail; + } + + c->sink_input = pa_sink_input_new(sink, c->client->name, &p->sample_spec); + if (!c->sink_input) { + fprintf(stderr, "Failed to create sink input.\n"); + goto fail; + } + c->sink_input->owner = p->module; + c->sink_input->client = c->client; + + c->sink_input->peek = sink_input_peek_cb; + c->sink_input->drop = sink_input_drop_cb; + c->sink_input->kill = sink_input_kill_cb; + c->sink_input->get_latency = sink_input_get_latency_cb; + c->sink_input->userdata = c; + + l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS); + c->input_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&p->sample_spec), l/2, l/PLAYBACK_BUFFER_FRAGMENTS); + assert(c->input_memblockq); + pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5); + c->playback.fragment_size = l/10; + } + + if (p->mode & RECORD) { + struct pa_source *source; + size_t l; + + if (!(source = pa_idxset_get_by_index(p->core->sources, p->source_index))) + if (!(source = pa_source_get_default(p->core))) { + fprintf(stderr, "Failed to get source.\n"); + goto fail; + } + + c->source_output = pa_source_output_new(source, c->client->name, &p->sample_spec); + if (!c->source_output) { + fprintf(stderr, "Failed to create source output.\n"); + goto fail; + } + c->source_output->owner = p->module; + c->source_output->client = c->client; + + c->source_output->push = source_output_push_cb; + c->source_output->kill = source_output_kill_cb; + c->source_output->userdata = c; + + l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS); + c->output_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&p->sample_spec), 0, 0); + pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2); + } + + pa_iochannel_set_callback(c->io, io_callback, c); + pa_idxset_put(p->connections, c, NULL); + + c->fixed_source = p->core->mainloop->source_fixed(p->core->mainloop, fixed_callback, c); + assert(c->fixed_source); + p->core->mainloop->enable_fixed(p->core->mainloop, c->fixed_source, 0); + + return; + +fail: + if (c) + connection_free(c); +} + +struct pa_protocol_simple* pa_protocol_simple_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) { + struct pa_protocol_simple* p = NULL; + uint32_t enable; + assert(core && server && ma); + + p = malloc(sizeof(struct pa_protocol_simple)); + assert(p); + memset(p, 0, sizeof(struct pa_protocol_simple)); + + p->module = m; + p->core = core; + p->server = server; + p->connections = pa_idxset_new(NULL, NULL); + + p->sample_spec = core->default_sample_spec; + if (pa_modargs_get_sample_spec(ma, &p->sample_spec) < 0) { + fprintf(stderr, "Failed to parse sample type specification.\n"); + goto fail; + } + + if (pa_modargs_get_source_index(ma, core, &p->source_index) < 0) { + fprintf(stderr, __FILE__": source does not exist.\n"); + goto fail; + } + + if (pa_modargs_get_sink_index(ma, core, &p->sink_index) < 0) { + fprintf(stderr, __FILE__": sink does not exist.\n"); + goto fail; + } + + enable = 0; + if (pa_modargs_get_value_u32(ma, "record", &enable) < 0) { + fprintf(stderr, __FILE__": record= expects a numeric argument.\n"); + goto fail; + } + p->mode = enable ? RECORD : 0; + + enable = 1; + if (pa_modargs_get_value_u32(ma, "playback", &enable) < 0) { + fprintf(stderr, __FILE__": playback= expects a numeric argument.\n"); + goto fail; + } + p->mode |= enable ? PLAYBACK : 0; + + if ((p->mode & (RECORD|PLAYBACK)) == 0) { + fprintf(stderr, __FILE__": neither playback nor recording enabled for protocol.\n"); + goto fail; + } + + pa_socket_server_set_callback(p->server, on_connection, p); + + return p; + +fail: + if (p) + pa_protocol_simple_free(p); + return NULL; +} + + +void pa_protocol_simple_free(struct pa_protocol_simple *p) { + struct connection *c; + assert(p); + + if (p->connections) { + while((c = pa_idxset_first(p->connections, NULL))) + connection_free(c); + + pa_idxset_free(p->connections, NULL, NULL); + } + + if (p->server) + pa_socket_server_free(p->server); + free(p); +} + diff --git a/polyp/protocol-simple.h b/polyp/protocol-simple.h new file mode 100644 index 00000000..0fc1e19d --- /dev/null +++ b/polyp/protocol-simple.h @@ -0,0 +1,35 @@ +#ifndef fooprotocolsimplehfoo +#define fooprotocolsimplehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "socket-server.h" +#include "module.h" +#include "core.h" +#include "modargs.h" + +struct pa_protocol_simple; + +struct pa_protocol_simple* pa_protocol_simple_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma); +void pa_protocol_simple_free(struct pa_protocol_simple *n); + +#endif diff --git a/polyp/pstream-util.c b/polyp/pstream-util.c new file mode 100644 index 00000000..3957e643 --- /dev/null +++ b/polyp/pstream-util.c @@ -0,0 +1,60 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "native-common.h" +#include "pstream-util.h" + +void pa_pstream_send_tagstruct(struct pa_pstream *p, struct pa_tagstruct *t) { + size_t length; + uint8_t *data; + struct pa_packet *packet; + assert(p && t); + + data = pa_tagstruct_free_data(t, &length); + assert(data && length); + packet = pa_packet_new_dynamic(data, length); + assert(packet); + pa_pstream_send_packet(p, packet); + pa_packet_unref(packet); +} + +void pa_pstream_send_error(struct pa_pstream *p, uint32_t tag, uint32_t error) { + struct pa_tagstruct *t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_ERROR); + pa_tagstruct_putu32(t, tag); + pa_tagstruct_putu32(t, error); + pa_pstream_send_tagstruct(p, t); +} + +void pa_pstream_send_simple_ack(struct pa_pstream *p, uint32_t tag) { + struct pa_tagstruct *t = pa_tagstruct_new(NULL, 0); + assert(t); + pa_tagstruct_putu32(t, PA_COMMAND_REPLY); + pa_tagstruct_putu32(t, tag); + pa_pstream_send_tagstruct(p, t); +} diff --git a/polyp/pstream-util.h b/polyp/pstream-util.h new file mode 100644 index 00000000..b3c89eb0 --- /dev/null +++ b/polyp/pstream-util.h @@ -0,0 +1,35 @@ +#ifndef foopstreamutilhfoo +#define foopstreamutilhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include "pstream.h" +#include "tagstruct.h" + +/* The tagstruct is freed!*/ +void pa_pstream_send_tagstruct(struct pa_pstream *p, struct pa_tagstruct *t); + +void pa_pstream_send_error(struct pa_pstream *p, uint32_t tag, uint32_t error); +void pa_pstream_send_simple_ack(struct pa_pstream *p, uint32_t tag); + +#endif diff --git a/polyp/pstream.c b/polyp/pstream.c new file mode 100644 index 00000000..3076b776 --- /dev/null +++ b/polyp/pstream.c @@ -0,0 +1,457 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "pstream.h" +#include "queue.h" + +enum pa_pstream_descriptor_index { + PA_PSTREAM_DESCRIPTOR_LENGTH, + PA_PSTREAM_DESCRIPTOR_CHANNEL, + PA_PSTREAM_DESCRIPTOR_DELTA, + PA_PSTREAM_DESCRIPTOR_MAX +}; + +typedef uint32_t pa_pstream_descriptor[PA_PSTREAM_DESCRIPTOR_MAX]; + +#define PA_PSTREAM_DESCRIPTOR_SIZE (PA_PSTREAM_DESCRIPTOR_MAX*sizeof(uint32_t)) +#define FRAME_SIZE_MAX (1024*64) + +struct item_info { + enum { PA_PSTREAM_ITEM_PACKET, PA_PSTREAM_ITEM_MEMBLOCK } type; + + /* memblock info */ + struct pa_memchunk chunk; + uint32_t channel; + int32_t delta; + + /* packet info */ + struct pa_packet *packet; +}; + +struct pa_pstream { + struct pa_mainloop_api *mainloop; + struct mainloop_source *mainloop_source; + struct pa_iochannel *io; + struct pa_queue *send_queue; + + int in_use, shall_free; + + int dead; + void (*die_callback) (struct pa_pstream *p, void *userdata); + void *die_callback_userdata; + + struct { + struct item_info* current; + pa_pstream_descriptor descriptor; + void *data; + size_t index; + } write; + + struct { + struct pa_memblock *memblock; + struct pa_packet *packet; + pa_pstream_descriptor descriptor; + void *data; + size_t index; + } read; + + void (*recieve_packet_callback) (struct pa_pstream *p, struct pa_packet *packet, void *userdata); + void *recieve_packet_callback_userdata; + + void (*recieve_memblock_callback) (struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata); + void *recieve_memblock_callback_userdata; + + void (*drain_callback)(struct pa_pstream *p, void *userdata); + void *drain_userdata; +}; + +static void do_write(struct pa_pstream *p); +static void do_read(struct pa_pstream *p); + +static void do_something(struct pa_pstream *p) { + assert(p && !p->shall_free); + p->mainloop->enable_fixed(p->mainloop, p->mainloop_source, 0); + + if (p->dead) + return; + + if (pa_iochannel_is_hungup(p->io)) { + p->dead = 1; + if (p->die_callback) + p->die_callback(p, p->die_callback_userdata); + + return; + } + + if (pa_iochannel_is_writable(p->io)) { + p->in_use = 1; + do_write(p); + p->in_use = 0; + + if (p->shall_free) { + pa_pstream_free(p); + return; + } + } + + if (pa_iochannel_is_readable(p->io)) { + p->in_use = 1; + do_read(p); + p->in_use = 0; + if (p->shall_free) { + pa_pstream_free(p); + return; + } + } +} + +static void io_callback(struct pa_iochannel*io, void *userdata) { + struct pa_pstream *p = userdata; + assert(p && p->io == io); + do_something(p); +} + +static void fixed_callback(struct pa_mainloop_api *m, void *id, void*userdata) { + struct pa_pstream *p = userdata; + assert(p && p->mainloop_source == id && p->mainloop == m); + do_something(p); +} + +struct pa_pstream *pa_pstream_new(struct pa_mainloop_api *m, struct pa_iochannel *io) { + struct pa_pstream *p; + assert(io); + + p = malloc(sizeof(struct pa_pstream)); + assert(p); + + p->io = io; + pa_iochannel_set_callback(io, io_callback, p); + + p->dead = 0; + p->die_callback = NULL; + p->die_callback_userdata = NULL; + + p->mainloop = m; + p->mainloop_source = m->source_fixed(m, fixed_callback, p); + m->enable_fixed(m, p->mainloop_source, 0); + + p->send_queue = pa_queue_new(); + assert(p->send_queue); + + p->write.current = NULL; + p->write.index = 0; + + p->read.memblock = NULL; + p->read.packet = NULL; + p->read.index = 0; + + p->recieve_packet_callback = NULL; + p->recieve_packet_callback_userdata = NULL; + + p->recieve_memblock_callback = NULL; + p->recieve_memblock_callback_userdata = NULL; + + p->drain_callback = NULL; + p->drain_userdata = NULL; + + p->in_use = p->shall_free = 0; + + return p; +} + +static void item_free(void *item, void *p) { + struct item_info *i = item; + assert(i); + + if (i->type == PA_PSTREAM_ITEM_MEMBLOCK) { + assert(i->chunk.memblock); + pa_memblock_unref(i->chunk.memblock); + } else { + assert(i->type == PA_PSTREAM_ITEM_PACKET); + assert(i->packet); + pa_packet_unref(i->packet); + } + + free(i); +} + +void pa_pstream_free(struct pa_pstream *p) { + assert(p); + + if (p->in_use) { + /* If this pstream object is used by someone else on the call stack, we have to postpone the freeing */ + p->dead = p->shall_free = 1; + return; + } + + pa_iochannel_free(p->io); + pa_queue_free(p->send_queue, item_free, NULL); + + if (p->write.current) + item_free(p->write.current, NULL); + + if (p->read.memblock) + pa_memblock_unref(p->read.memblock); + + if (p->read.packet) + pa_packet_unref(p->read.packet); + + p->mainloop->cancel_fixed(p->mainloop, p->mainloop_source); + free(p); +} + +void pa_pstream_send_packet(struct pa_pstream*p, struct pa_packet *packet) { + struct item_info *i; + assert(p && packet); + + i = malloc(sizeof(struct item_info)); + assert(i); + i->type = PA_PSTREAM_ITEM_PACKET; + i->packet = pa_packet_ref(packet); + + pa_queue_push(p->send_queue, i); + p->mainloop->enable_fixed(p->mainloop, p->mainloop_source, 1); +} + +void pa_pstream_send_memblock(struct pa_pstream*p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk) { + struct item_info *i; + assert(p && channel != (uint32_t) -1 && chunk); + + i = malloc(sizeof(struct item_info)); + assert(i); + i->type = PA_PSTREAM_ITEM_MEMBLOCK; + i->chunk = *chunk; + i->channel = channel; + i->delta = delta; + + pa_memblock_ref(i->chunk.memblock); + + pa_queue_push(p->send_queue, i); + p->mainloop->enable_fixed(p->mainloop, p->mainloop_source, 1); +} + +void pa_pstream_set_recieve_packet_callback(struct pa_pstream *p, void (*callback) (struct pa_pstream *p, struct pa_packet *packet, void *userdata), void *userdata) { + assert(p && callback); + + p->recieve_packet_callback = callback; + p->recieve_packet_callback_userdata = userdata; +} + +void pa_pstream_set_recieve_memblock_callback(struct pa_pstream *p, void (*callback) (struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata), void *userdata) { + assert(p && callback); + + p->recieve_memblock_callback = callback; + p->recieve_memblock_callback_userdata = userdata; +} + +static void prepare_next_write_item(struct pa_pstream *p) { + assert(p); + + if (!(p->write.current = pa_queue_pop(p->send_queue))) + return; + + p->write.index = 0; + + if (p->write.current->type == PA_PSTREAM_ITEM_PACKET) { + assert(p->write.current->packet); + p->write.data = p->write.current->packet->data; + p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->packet->length); + p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1); + p->write.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA] = 0; + } else { + assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK && p->write.current->chunk.memblock); + p->write.data = p->write.current->chunk.memblock->data + p->write.current->chunk.index; + p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->chunk.length); + p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl(p->write.current->channel); + p->write.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA] = htonl(p->write.current->delta); + } +} + +static void do_write(struct pa_pstream *p) { + void *d; + size_t l; + ssize_t r; + assert(p); + + if (!p->write.current) + prepare_next_write_item(p); + + if (!p->write.current) + return; + + assert(p->write.data); + + if (p->write.index < PA_PSTREAM_DESCRIPTOR_SIZE) { + d = (void*) p->write.descriptor + p->write.index; + l = PA_PSTREAM_DESCRIPTOR_SIZE - p->write.index; + } else { + d = (void*) p->write.data + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE; + l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE); + } + + if ((r = pa_iochannel_write(p->io, d, l)) < 0) + goto die; + + p->write.index += r; + + if (p->write.index >= PA_PSTREAM_DESCRIPTOR_SIZE+ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])) { + assert(p->write.current); + item_free(p->write.current, (void *) 1); + p->write.current = NULL; + + if (p->drain_callback && !pa_pstream_is_pending(p)) + p->drain_callback(p, p->drain_userdata); + } + + return; + +die: + p->dead = 1; + if (p->die_callback) + p->die_callback(p, p->die_callback_userdata); +} + +static void do_read(struct pa_pstream *p) { + void *d; + size_t l; + ssize_t r; + assert(p); + + if (p->read.index < PA_PSTREAM_DESCRIPTOR_SIZE) { + d = (void*) p->read.descriptor + p->read.index; + l = PA_PSTREAM_DESCRIPTOR_SIZE - p->read.index; + } else { + assert(p->read.data); + d = (void*) p->read.data + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE; + l = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE); + } + + if ((r = pa_iochannel_read(p->io, d, l)) <= 0) + goto die; + + p->read.index += r; + + if (p->read.index == PA_PSTREAM_DESCRIPTOR_SIZE) { + /* Reading of frame descriptor complete */ + + /* Frame size too large */ + if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) > FRAME_SIZE_MAX) + goto die; + + assert(!p->read.packet && !p->read.memblock); + + if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]) == (uint32_t) -1) { + /* Frame is a packet frame */ + p->read.packet = pa_packet_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])); + assert(p->read.packet); + p->read.data = p->read.packet->data; + } else { + /* Frame is a memblock frame */ + p->read.memblock = pa_memblock_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])); + assert(p->read.memblock); + p->read.data = p->read.memblock->data; + } + + } else if (p->read.index > PA_PSTREAM_DESCRIPTOR_SIZE) { + /* Frame payload available */ + + if (p->read.memblock && p->recieve_memblock_callback) { /* Is this memblock data? Than pass it to the user */ + size_t l; + + l = (p->read.index - r) < PA_PSTREAM_DESCRIPTOR_SIZE ? p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE : (size_t) r; + + if (l > 0) { + struct pa_memchunk chunk; + + chunk.memblock = p->read.memblock; + chunk.index = p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE - l; + chunk.length = l; + + if (p->recieve_memblock_callback) + p->recieve_memblock_callback( + p, + ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]), + (int32_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA]), + &chunk, + p->recieve_memblock_callback_userdata); + } + } + + /* Frame complete */ + if (p->read.index >= ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) + PA_PSTREAM_DESCRIPTOR_SIZE) { + if (p->read.memblock) { + assert(!p->read.packet); + + pa_memblock_unref(p->read.memblock); + p->read.memblock = NULL; + } else { + assert(p->read.packet); + + if (p->recieve_packet_callback) + p->recieve_packet_callback(p, p->read.packet, p->recieve_packet_callback_userdata); + + pa_packet_unref(p->read.packet); + p->read.packet = NULL; + } + + p->read.index = 0; + } + } + + return; + +die: + p->dead = 1; + if (p->die_callback) + p->die_callback(p, p->die_callback_userdata); + +} + +void pa_pstream_set_die_callback(struct pa_pstream *p, void (*callback)(struct pa_pstream *p, void *userdata), void *userdata) { + assert(p && callback); + p->die_callback = callback; + p->die_callback_userdata = userdata; +} + +int pa_pstream_is_pending(struct pa_pstream *p) { + assert(p); + + if (p->dead) + return 0; + + return p->write.current || !pa_queue_is_empty(p->send_queue); +} + +void pa_pstream_set_drain_callback(struct pa_pstream *p, void (*cb)(struct pa_pstream *p, void *userdata), void *userdata) { + assert(p); + + p->drain_callback = cb; + p->drain_userdata = userdata; +} + diff --git a/polyp/pstream.h b/polyp/pstream.h new file mode 100644 index 00000000..6b91aeb0 --- /dev/null +++ b/polyp/pstream.h @@ -0,0 +1,51 @@ +#ifndef foopstreamhfoo +#define foopstreamhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "packet.h" +#include "memblock.h" +#include "iochannel.h" +#include "mainloop-api.h" +#include "memchunk.h" + +/* It is safe to destroy the calling pstream object from all callbacks */ + +struct pa_pstream; + +struct pa_pstream* pa_pstream_new(struct pa_mainloop_api *m, struct pa_iochannel *io); +void pa_pstream_free(struct pa_pstream*p); + +void pa_pstream_send_packet(struct pa_pstream*p, struct pa_packet *packet); +void pa_pstream_send_memblock(struct pa_pstream*p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk); + +void pa_pstream_set_recieve_packet_callback(struct pa_pstream *p, void (*callback) (struct pa_pstream *p, struct pa_packet *packet, void *userdata), void *userdata); +void pa_pstream_set_recieve_memblock_callback(struct pa_pstream *p, void (*callback) (struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata), void *userdata); +void pa_pstream_set_drain_callback(struct pa_pstream *p, void (*cb)(struct pa_pstream *p, void *userdata), void *userdata); + +void pa_pstream_set_die_callback(struct pa_pstream *p, void (*callback)(struct pa_pstream *p, void *userdata), void *userdata); + +int pa_pstream_is_pending(struct pa_pstream *p); + +#endif diff --git a/polyp/queue.c b/polyp/queue.c new file mode 100644 index 00000000..9befd475 --- /dev/null +++ b/polyp/queue.c @@ -0,0 +1,109 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "queue.h" + +struct queue_entry { + struct queue_entry *next; + void *data; +}; + +struct pa_queue { + struct queue_entry *front, *back; + unsigned length; +}; + +struct pa_queue* pa_queue_new(void) { + struct pa_queue *q = malloc(sizeof(struct pa_queue)); + assert(q); + q->front = q->back = NULL; + q->length = 0; + return q; +} + +void pa_queue_free(struct pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata) { + struct queue_entry *e; + assert(q); + + e = q->front; + while (e) { + struct queue_entry *n = e->next; + + if (destroy) + destroy(e->data, userdata); + + free(e); + e = n; + } + + free(q); +} + +void pa_queue_push(struct pa_queue *q, void *p) { + struct queue_entry *e; + + e = malloc(sizeof(struct queue_entry)); + + e->data = p; + e->next = NULL; + + if (q->back) + q->back->next = e; + else { + assert(!q->front); + q->front = e; + } + + q->back = e; + q->length++; +} + +void* pa_queue_pop(struct pa_queue *q) { + void *p; + struct queue_entry *e; + assert(q); + + if (!(e = q->front)) + return NULL; + + q->front = e->next; + if (q->back == e) + q->back = NULL; + + p = e->data; + free(e); + + q->length--; + + return p; +} + +int pa_queue_is_empty(struct pa_queue *q) { + assert(q); + return q->length == 0; +} diff --git a/polyp/queue.h b/polyp/queue.h new file mode 100644 index 00000000..3ec13734 --- /dev/null +++ b/polyp/queue.h @@ -0,0 +1,34 @@ +#ifndef fooqueuehfoo +#define fooqueuehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +struct pa_queue; + +struct pa_queue* pa_queue_new(void); +void pa_queue_free(struct pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata); +void pa_queue_push(struct pa_queue *q, void *p); +void* pa_queue_pop(struct pa_queue *q); + +int pa_queue_is_empty(struct pa_queue *q); + +#endif diff --git a/polyp/resampler.c b/polyp/resampler.c new file mode 100644 index 00000000..4f5f6be3 --- /dev/null +++ b/polyp/resampler.c @@ -0,0 +1,180 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +#include "resampler.h" +#include "sconv.h" + +struct pa_resampler { + struct pa_sample_spec i_ss, o_ss; + float* i_buf, *o_buf; + unsigned i_alloc, o_alloc; + size_t i_sz, o_sz; + + int channels; + + pa_convert_to_float32_func_t to_float32_func; + pa_convert_from_float32_func_t from_float32_func; + SRC_STATE *src_state; +}; + +struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const struct pa_sample_spec *b) { + struct pa_resampler *r; + int err; + assert(a && b && pa_sample_spec_valid(a) && pa_sample_spec_valid(b)); + + if (a->channels != b->channels && a->channels != 1 && b->channels != 1) + goto fail; + + if (a->format == PA_SAMPLE_ALAW || a->format == PA_SAMPLE_ULAW || b->format == PA_SAMPLE_ALAW || b->format == PA_SAMPLE_ULAW) + goto fail; + + r = malloc(sizeof(struct pa_resampler)); + assert(r); + + r->channels = a->channels; + if (b->channels < r->channels) + r->channels = b->channels; + + r->i_buf = r->o_buf = NULL; + r->i_alloc = r->o_alloc = 0; + + if (a->rate != b->rate) { + r->src_state = src_new(SRC_SINC_FASTEST, r->channels, &err); + if (err != 0 || !r->src_state) + goto fail; + } else + r->src_state = NULL; + + r->i_ss = *a; + r->o_ss = *b; + + r->i_sz = pa_sample_size(a); + r->o_sz = pa_sample_size(b); + + r->to_float32_func = pa_get_convert_to_float32_function(a->format); + r->from_float32_func = pa_get_convert_from_float32_function(b->format); + + assert(r->to_float32_func && r->from_float32_func); + + return r; + +fail: + if (r) + free(r); + + return NULL; +} + +void pa_resampler_free(struct pa_resampler *r) { + assert(r); + if (r->src_state) + src_delete(r->src_state); + free(r->i_buf); + free(r->o_buf); + free(r); +} + +size_t pa_resampler_request(struct pa_resampler *r, size_t out_length) { + assert(r && (out_length % r->o_sz) == 0); + + return (((out_length / r->o_sz)*r->i_ss.rate)/r->o_ss.rate) * r->i_sz; +} + + +void pa_resampler_run(struct pa_resampler *r, const struct pa_memchunk *in, struct pa_memchunk *out) { + unsigned i_nchannels, o_nchannels, ins, ons, eff_ins, eff_ons; + float *cbuf; + assert(r && in && out && in->length && in->memblock && (in->length % r->i_sz) == 0); + + /* How many input samples? */ + ins = in->length/r->i_sz; + + /* How much space for output samples? */ + if (r->src_state) + ons = (ins*r->o_ss.rate/r->i_ss.rate)+1024; + else + ons = ins; + + /* How many channels? */ + if (r->i_ss.channels == r->o_ss.channels) { + i_nchannels = o_nchannels = 1; + eff_ins = ins*r->i_ss.channels; /* effective samples */ + eff_ons = ons*r->o_ss.channels; + } else { + i_nchannels = r->i_ss.channels; + o_nchannels = r->o_ss.channels; + eff_ins = ins; + eff_ons = ons; + } + + out->memblock = pa_memblock_new(out->length = (ons*r->o_sz)); + out->index = 0; + assert(out->memblock); + + if (r->i_alloc < eff_ins) + r->i_buf = realloc(r->i_buf, sizeof(float) * (r->i_alloc = eff_ins)); + assert(r->i_buf); + + r->to_float32_func(eff_ins, in->memblock->data+in->index, i_nchannels, r->i_buf); + + if (r->src_state) { + int ret; + SRC_DATA data; + + if (r->o_alloc < eff_ons) + r->o_buf = realloc(r->o_buf, sizeof(float) * (r->o_alloc = eff_ons)); + assert(r->o_buf); + + data.data_in = r->i_buf; + data.input_frames = ins; + + data.data_out = r->o_buf; + data.output_frames = ons; + + data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate; + data.end_of_input = 0; + + ret = src_process(r->src_state, &data); + assert(ret == 0); + assert((unsigned) data.input_frames_used == ins); + + cbuf = r->o_buf; + ons = data.output_frames_gen; + + if (r->i_ss.channels == r->o_ss.channels) + eff_ons = ons*r->o_ss.channels; + else + eff_ons = ons; + } else + cbuf = r->i_buf; + + r->from_float32_func(eff_ons, cbuf, out->memblock->data+out->index, o_nchannels); + out->length = ons*r->o_sz; +} diff --git a/polyp/resampler.h b/polyp/resampler.h new file mode 100644 index 00000000..8e979478 --- /dev/null +++ b/polyp/resampler.h @@ -0,0 +1,37 @@ +#ifndef fooresamplerhfoo +#define fooresamplerhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "sample.h" +#include "memblock.h" +#include "memchunk.h" + +struct pa_resampler; + +struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const struct pa_sample_spec *b); +void pa_resampler_free(struct pa_resampler *r); + +size_t pa_resampler_request(struct pa_resampler *r, size_t out_length); +void pa_resampler_run(struct pa_resampler *r, const struct pa_memchunk *in, struct pa_memchunk *out); + +#endif diff --git a/polyp/sample-util.c b/polyp/sample-util.c new file mode 100644 index 00000000..d608ce1b --- /dev/null +++ b/polyp/sample-util.c @@ -0,0 +1,144 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "sample-util.h" + +struct pa_memblock *pa_silence_memblock(struct pa_memblock* b, const struct pa_sample_spec *spec) { + assert(b && b->data && spec); + pa_silence_memory(b->data, b->length, spec); + return b; +} + +void pa_silence_memchunk(struct pa_memchunk *c, const struct pa_sample_spec *spec) { + assert(c && c->memblock && c->memblock->data && spec && c->length); + pa_silence_memory(c->memblock->data+c->index, c->length, spec); +} + +void pa_silence_memory(void *p, size_t length, const struct pa_sample_spec *spec) { + char c = 0; + assert(p && length && spec); + + switch (spec->format) { + case PA_SAMPLE_U8: + c = 127; + break; + case PA_SAMPLE_S16LE: + case PA_SAMPLE_S16BE: + case PA_SAMPLE_FLOAT32: + c = 0; + break; + case PA_SAMPLE_ALAW: + case PA_SAMPLE_ULAW: + c = 80; + break; + default: + assert(0); + } + + memset(p, c, length); +} + +size_t pa_mix(struct pa_mix_info channels[], unsigned nchannels, void *data, size_t length, const struct pa_sample_spec *spec, uint32_t volume) { + unsigned c, d; + assert(channels && data && length && spec); + assert(spec->format == PA_SAMPLE_S16NE); + + for (d = 0;; d += sizeof(int16_t)) { + int32_t sum = 0; + + if (d >= length) + return d; + + for (c = 0; c < nchannels; c++) { + int32_t v; + uint32_t volume = channels[c].volume; + + if (d >= channels[c].chunk.length) + return d; + + if (volume == PA_VOLUME_MUTE) + v = 0; + else { + v = *((int16_t*) (channels[c].chunk.memblock->data + channels[c].chunk.index + d)); + + if (volume != PA_VOLUME_NORM) + v = (int32_t) ((float)v*volume/PA_VOLUME_NORM); + } + + sum += v; + } + + if (volume == PA_VOLUME_MUTE) + sum = 0; + else if (volume != PA_VOLUME_NORM) + sum = (int32_t) ((float) sum*volume/PA_VOLUME_NORM); + + if (sum < -0x8000) sum = -0x8000; + if (sum > 0x7FFF) sum = 0x7FFF; + + *((int16_t*) data) = sum; + data += sizeof(int16_t); + } +} + + +void pa_volume_memchunk(struct pa_memchunk*c, const struct pa_sample_spec *spec, uint32_t volume) { + int16_t *d; + size_t n; + assert(c && spec && (c->length % pa_sample_size(spec) == 0)); + assert(spec->format == PA_SAMPLE_S16NE); + + if (volume == PA_VOLUME_NORM) + return; + + if (volume == PA_VOLUME_MUTE) { + pa_silence_memchunk(c, spec); + return; + } + + for (d = (c->memblock->data+c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) { + int32_t t = (int32_t)(*d); + + t *= volume; + t /= PA_VOLUME_NORM; + + if (t < -0x8000) t = -0x8000; + if (t > 0x7FFF) t = 0x7FFF; + + *d = (int16_t) t; + } +} + +uint32_t pa_volume_multiply(uint32_t a, uint32_t b) { + uint64_t p = a; + p *= b; + p /= PA_VOLUME_NORM; + + return (uint32_t) p; +} diff --git a/polyp/sample-util.h b/polyp/sample-util.h new file mode 100644 index 00000000..73101ab4 --- /dev/null +++ b/polyp/sample-util.h @@ -0,0 +1,48 @@ +#ifndef foosampleutilhfoo +#define foosampleutilhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "sample.h" +#include "memblock.h" +#include "memchunk.h" + +#define PA_VOLUME_NORM (0x100) +#define PA_VOLUME_MUTE (0) + +struct pa_memblock *pa_silence_memblock(struct pa_memblock* b, const struct pa_sample_spec *spec); +void pa_silence_memchunk(struct pa_memchunk *c, const struct pa_sample_spec *spec); +void pa_silence_memory(void *p, size_t length, const struct pa_sample_spec *spec); + +struct pa_mix_info { + struct pa_memchunk chunk; + uint32_t volume; + void *userdata; +}; + +size_t pa_mix(struct pa_mix_info channels[], unsigned nchannels, void *data, size_t length, const struct pa_sample_spec *spec, uint32_t volume); + +void pa_volume_memchunk(struct pa_memchunk*c, const struct pa_sample_spec *spec, uint32_t volume); + +uint32_t pa_volume_multiply(uint32_t a, uint32_t b); + +#endif diff --git a/polyp/sample.c b/polyp/sample.c new file mode 100644 index 00000000..8179475d --- /dev/null +++ b/polyp/sample.c @@ -0,0 +1,99 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "sample.h" + +size_t pa_sample_size(const struct pa_sample_spec *spec) { + assert(spec); + size_t b = 1; + + switch (spec->format) { + case PA_SAMPLE_U8: + case PA_SAMPLE_ULAW: + case PA_SAMPLE_ALAW: + b = 1; + break; + case PA_SAMPLE_S16LE: + case PA_SAMPLE_S16BE: + b = 2; + break; + case PA_SAMPLE_FLOAT32LE: + case PA_SAMPLE_FLOAT32BE: + b = 4; + break; + default: + assert(0); + } + + return b * spec->channels; +} + +size_t pa_bytes_per_second(const struct pa_sample_spec *spec) { + assert(spec); + return spec->rate*pa_sample_size(spec); +} + + +uint32_t pa_samples_usec(size_t length, const struct pa_sample_spec *spec) { + assert(spec); + + return (uint32_t) (((double) length /pa_sample_size(spec))/spec->rate*1000000); +} + +int pa_sample_spec_valid(const struct pa_sample_spec *spec) { + assert(spec); + + if (!spec->rate || !spec->channels) + return 0; + + if (spec->format >= PA_SAMPLE_MAX) + return 0; + + return 1; +} + +int pa_sample_spec_equal(const struct pa_sample_spec*a, const struct pa_sample_spec*b) { + assert(a && b); + + return (a->format == b->format) && (a->rate == b->rate) && (a->channels == b->channels); +} + +void pa_sample_snprint(char *s, size_t l, const struct pa_sample_spec *spec) { + static const char* const table[]= { + [PA_SAMPLE_U8] = "U8", + [PA_SAMPLE_ALAW] = "ALAW", + [PA_SAMPLE_ULAW] = "ULAW", + [PA_SAMPLE_S16LE] = "S16LE", + [PA_SAMPLE_S16BE] = "S16BE", + [PA_SAMPLE_FLOAT32LE] = "FLOAT32LE", + [PA_SAMPLE_FLOAT32BE] = "FLOAT32BE", + }; + + assert(pa_sample_spec_valid(spec)); + snprintf(s, l, "%s %uch %uHz", table[spec->format], spec->channels, spec->rate); +} diff --git a/polyp/sample.h b/polyp/sample.h new file mode 100644 index 00000000..825441f2 --- /dev/null +++ b/polyp/sample.h @@ -0,0 +1,64 @@ +#ifndef foosamplehfoo +#define foosamplehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +enum pa_sample_format { + PA_SAMPLE_U8, + PA_SAMPLE_ALAW, + PA_SAMPLE_ULAW, + PA_SAMPLE_S16LE, + PA_SAMPLE_S16BE, + PA_SAMPLE_FLOAT32LE, + PA_SAMPLE_FLOAT32BE, + PA_SAMPLE_MAX +}; + +#ifdef WORDS_BIGENDIAN +#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE +#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE +#else +#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE +#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE +#endif +#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32NE + +struct pa_sample_spec { + enum pa_sample_format format; + uint32_t rate; + uint8_t channels; +}; + +size_t pa_bytes_per_second(const struct pa_sample_spec *spec); +size_t pa_sample_size(const struct pa_sample_spec *spec); +uint32_t pa_samples_usec(size_t length, const struct pa_sample_spec *spec); +int pa_sample_spec_valid(const struct pa_sample_spec *spec); +int pa_sample_spec_equal(const struct pa_sample_spec*a, const struct pa_sample_spec*b); + + +#define PA_SAMPLE_SNPRINT_MAX_LENGTH 32 +void pa_sample_snprint(char *s, size_t l, const struct pa_sample_spec *spec); + +#endif diff --git a/polyp/sconv-s16be.c b/polyp/sconv-s16be.c new file mode 100644 index 00000000..a4c25cde --- /dev/null +++ b/polyp/sconv-s16be.c @@ -0,0 +1,34 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "sconv-s16be.h" + +#define INT16_FROM INT16_FROM_BE +#define INT16_TO INT16_TO_BE + +#define pa_sconv_s16le_to_float32 pa_sconv_s16be_to_float32 +#define pa_sconv_s16le_from_float32 pa_sconv_s16be_from_float32 + +#include "sconv-s16le.c" diff --git a/polyp/sconv-s16be.h b/polyp/sconv-s16be.h new file mode 100644 index 00000000..d112d9f2 --- /dev/null +++ b/polyp/sconv-s16be.h @@ -0,0 +1,28 @@ +#ifndef foosconv_s16befoo +#define foosconv_s16befoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +void pa_sconv_s16be_to_float32(unsigned n, const void *a, unsigned an, float *b); +void pa_sconv_s16be_from_float32(unsigned n, const float *a, void *b, unsigned bn); + +#endif diff --git a/polyp/sconv-s16le.c b/polyp/sconv-s16le.c new file mode 100644 index 00000000..45b28bdb --- /dev/null +++ b/polyp/sconv-s16le.c @@ -0,0 +1,82 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "endianmacros.h" +#include "sconv.h" + +#ifndef INT16_FROM +#define INT16_FROM INT16_FROM_LE +#endif + +#ifndef INT16_TO +#define INT16_TO INT16_TO_LE +#endif + +void pa_sconv_s16le_to_float32(unsigned n, const void *a, unsigned an, float *b) { + const int16_t *ca = a; + assert(n && a && an && b); + + for (; n > 0; n--) { + unsigned i; + float sum = 0; + + for (i = 0; i < an; i++) { + int16_t s = *(ca++); + sum += ((float) INT16_FROM(s))/0x7FFF; + } + + if (sum > 1) + sum = 1; + if (sum < -1) + sum = -1; + + *(b++) = sum; + } +} + +void pa_sconv_s16le_from_float32(unsigned n, const float *a, void *b, unsigned bn) { + int16_t *cb = b; + assert(n && a && b && bn); + + for (; n > 0; n--) { + unsigned i; + int16_t s; + float v = *(a++); + + if (v > 1) + v = 1; + if (v < -1) + v = -1; + + s = (int16_t) (v * 0x7FFF); + s = INT16_TO(s); + + for (i = 0; i < bn; i++) + *(cb++) = s; + } +} diff --git a/polyp/sconv-s16le.h b/polyp/sconv-s16le.h new file mode 100644 index 00000000..0f206ec3 --- /dev/null +++ b/polyp/sconv-s16le.h @@ -0,0 +1,28 @@ +#ifndef foosconv_s16lefoo +#define foosconv_s16lefoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +void pa_sconv_s16le_to_float32(unsigned n, const void *a, unsigned an, float *b); +void pa_sconv_s16le_from_float32(unsigned n, const float *a, void *b, unsigned bn); + +#endif diff --git a/polyp/sconv.c b/polyp/sconv.c new file mode 100644 index 00000000..dd9dd241 --- /dev/null +++ b/polyp/sconv.c @@ -0,0 +1,137 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include "endianmacros.h" +#include "sconv.h" + +#include "sconv-s16le.h" +#include "sconv-s16be.h" + +static void u8_to_float32(unsigned n, const void *a, unsigned an, float *b) { + unsigned i; + const uint8_t *ca = a; + assert(n && a && an && b); + + for (; n > 0; n--) { + float sum = 0; + + for (i = 0; i < an; i++) { + uint8_t v = *(ca++); + sum += (((float) v)-127)/127; + } + + if (sum > 1) + sum = 1; + if (sum < -1) + sum = -1; + + *(b++) = sum; + } +} + +static void u8_from_float32(unsigned n, const float *a, void *b, unsigned bn) { + unsigned i; + uint8_t *cb = b; + + assert(n && a && b && bn); + for (; n > 0; n--) { + float v = *(a++); + uint8_t u; + + if (v > 1) + v = 1; + + if (v < -1) + v = -1; + + u = (uint8_t) (v*127+127); + + for (i = 0; i < bn; i++) + *(cb++) = u; + } +} + +static void float32_to_float32(unsigned n, const void *a, unsigned an, float *b) { + unsigned i; + const float *ca = a; + assert(n && a && an && b); + for (; n > 0; n--) { + float sum = 0; + + for (i = 0; i < an; i++) + sum += *(ca++); + + if (sum > 1) + sum = 1; + if (sum < -1) + sum = -1; + + *(b++) = sum; + } +} + +static void float32_from_float32(unsigned n, const float *a, void *b, unsigned bn) { + unsigned i; + float *cb = b; + assert(n && a && b && bn); + for (; n > 0; n--) { + float v = *(a++); + for (i = 0; i < bn; i++) + *(cb++) = v; + } +} + +pa_convert_to_float32_func_t pa_get_convert_to_float32_function(enum pa_sample_format f) { + switch(f) { + case PA_SAMPLE_U8: + return u8_to_float32; + case PA_SAMPLE_S16LE: + return pa_sconv_s16le_to_float32; + case PA_SAMPLE_S16BE: + return pa_sconv_s16be_to_float32; + case PA_SAMPLE_FLOAT32: + return float32_to_float32; + default: + return NULL; + } +} + +pa_convert_from_float32_func_t pa_get_convert_from_float32_function(enum pa_sample_format f) { + switch(f) { + case PA_SAMPLE_U8: + return u8_from_float32; + case PA_SAMPLE_S16LE: + return pa_sconv_s16le_from_float32; + case PA_SAMPLE_S16BE: + return pa_sconv_s16be_from_float32; + case PA_SAMPLE_FLOAT32: + return float32_from_float32; + default: + return NULL; + } +} diff --git a/polyp/sconv.h b/polyp/sconv.h new file mode 100644 index 00000000..1a62ed20 --- /dev/null +++ b/polyp/sconv.h @@ -0,0 +1,33 @@ +#ifndef foosconvhfoo +#define foosconvhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "sample.h" + +typedef void (*pa_convert_to_float32_func_t)(unsigned n, const void *a, unsigned an, float *b); +typedef void (*pa_convert_from_float32_func_t)(unsigned n, const float *a, void *b, unsigned bn); + +pa_convert_to_float32_func_t pa_get_convert_to_float32_function(enum pa_sample_format f); +pa_convert_from_float32_func_t pa_get_convert_from_float32_function(enum pa_sample_format f); + +#endif diff --git a/polyp/sink-input.c b/polyp/sink-input.c new file mode 100644 index 00000000..5c2d3a13 --- /dev/null +++ b/polyp/sink-input.c @@ -0,0 +1,163 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "sink-input.h" +#include "sample-util.h" + +#define CONVERT_BUFFER_LENGTH 4096 + +struct pa_sink_input* pa_sink_input_new(struct pa_sink *s, const char *name, const struct pa_sample_spec *spec) { + struct pa_sink_input *i; + struct pa_resampler *resampler = NULL; + int r; + char st[256]; + assert(s && spec); + + if (!pa_sample_spec_equal(spec, &s->sample_spec)) + if (!(resampler = pa_resampler_new(spec, &s->sample_spec))) + return NULL; + + i = malloc(sizeof(struct pa_sink_input)); + assert(i); + i->name = name ? strdup(name) : NULL; + i->client = NULL; + i->owner = NULL; + i->sink = s; + i->sample_spec = *spec; + + i->peek = NULL; + i->drop = NULL; + i->kill = NULL; + i->get_latency = NULL; + i->userdata = NULL; + + i->volume = PA_VOLUME_NORM; + + i->resampled_chunk.memblock = NULL; + i->resampled_chunk.index = i->resampled_chunk.length = 0; + i->resampler = resampler; + + assert(s->core); + r = pa_idxset_put(s->core->sink_inputs, i, &i->index); + assert(r == 0 && i->index != PA_IDXSET_INVALID); + r = pa_idxset_put(s->inputs, i, NULL); + assert(r == 0); + + pa_sample_snprint(st, sizeof(st), spec); + fprintf(stderr, "sink-input: created %u \"%s\" on %u with sample spec \"%s\"\n", i->index, i->name, s->index, st); + + return i; +} + +void pa_sink_input_free(struct pa_sink_input* i) { + assert(i); + + assert(i->sink && i->sink->core); + pa_idxset_remove_by_data(i->sink->core->sink_inputs, i, NULL); + pa_idxset_remove_by_data(i->sink->inputs, i, NULL); + + if (i->resampled_chunk.memblock) + pa_memblock_unref(i->resampled_chunk.memblock); + if (i->resampler) + pa_resampler_free(i->resampler); + + free(i->name); + free(i); +} + +void pa_sink_input_kill(struct pa_sink_input*i) { + assert(i); + + if (i->kill) + i->kill(i); +} + +uint32_t pa_sink_input_get_latency(struct pa_sink_input *i) { + uint32_t l = 0; + + assert(i); + if (i->get_latency) + l += i->get_latency(i); + + assert(i->sink); + l += pa_sink_get_latency(i->sink); + + return l; +} + +int pa_sink_input_peek(struct pa_sink_input *i, struct pa_memchunk *chunk) { + assert(i && chunk && i->peek && i->drop); + + if (!i->resampler) + return i->peek(i, chunk); + + if (!i->resampled_chunk.memblock) { + struct pa_memchunk tchunk; + size_t l; + int ret; + + if ((ret = i->peek(i, &tchunk)) < 0) + return ret; + + l = pa_resampler_request(i->resampler, CONVERT_BUFFER_LENGTH); + if (tchunk.length > l) + tchunk.length = l; + + i->drop(i, tchunk.length); + + pa_resampler_run(i->resampler, &tchunk, &i->resampled_chunk); + pa_memblock_unref(tchunk.memblock); + } + + assert(i->resampled_chunk.memblock && i->resampled_chunk.length); + *chunk = i->resampled_chunk; + pa_memblock_ref(i->resampled_chunk.memblock); + return 0; +} + +void pa_sink_input_drop(struct pa_sink_input *i, size_t length) { + assert(i && length); + + if (!i->resampler) { + i->drop(i, length); + return; + } + + assert(i->resampled_chunk.memblock && i->resampled_chunk.length >= length); + + i->resampled_chunk.index += length; + i->resampled_chunk.length -= length; + + if (!i->resampled_chunk.length) { + pa_memblock_unref(i->resampled_chunk.memblock); + i->resampled_chunk.memblock = NULL; + i->resampled_chunk.index = i->resampled_chunk.length = 0; + } +} diff --git a/polyp/sink-input.h b/polyp/sink-input.h new file mode 100644 index 00000000..63dce71d --- /dev/null +++ b/polyp/sink-input.h @@ -0,0 +1,67 @@ +#ifndef foosinkinputhfoo +#define foosinkinputhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "sink.h" +#include "sample.h" +#include "memblockq.h" +#include "resampler.h" +#include "module.h" +#include "client.h" + +struct pa_sink_input { + uint32_t index; + + char *name; + struct pa_module *owner; + struct pa_client *client; + struct pa_sink *sink; + struct pa_sample_spec sample_spec; + uint32_t volume; + + int (*peek) (struct pa_sink_input *i, struct pa_memchunk *chunk); + void (*drop) (struct pa_sink_input *i, size_t length); + void (*kill) (struct pa_sink_input *i); + uint32_t (*get_latency) (struct pa_sink_input *i); + + void *userdata; + + struct pa_memchunk resampled_chunk; + struct pa_resampler *resampler; +}; + +struct pa_sink_input* pa_sink_input_new(struct pa_sink *s, const char *name, const struct pa_sample_spec *spec); +void pa_sink_input_free(struct pa_sink_input* i); + +/* Code that didn't create the input stream should call this function to + * request destruction of it */ +void pa_sink_input_kill(struct pa_sink_input *i); + +uint32_t pa_sink_input_get_latency(struct pa_sink_input *i); + +int pa_sink_input_peek(struct pa_sink_input *i, struct pa_memchunk *chunk); +void pa_sink_input_drop(struct pa_sink_input *i, size_t length); + +#endif diff --git a/polyp/sink.c b/polyp/sink.c new file mode 100644 index 00000000..20fa76a6 --- /dev/null +++ b/polyp/sink.c @@ -0,0 +1,292 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "sink.h" +#include "sink-input.h" +#include "namereg.h" +#include "util.h" +#include "sample-util.h" + +#define MAX_MIX_CHANNELS 32 + +struct pa_sink* pa_sink_new(struct pa_core *core, const char *name, int fail, const struct pa_sample_spec *spec) { + struct pa_sink *s; + char *n = NULL; + char st[256]; + int r; + assert(core && name && spec); + + s = malloc(sizeof(struct pa_sink)); + assert(s); + + if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) { + free(s); + return NULL; + } + + s->name = strdup(name); + s->description = NULL; + + s->owner = NULL; + s->core = core; + s->sample_spec = *spec; + s->inputs = pa_idxset_new(NULL, NULL); + + n = pa_sprintf_malloc("%s_monitor", name); + s->monitor_source = pa_source_new(core, n, 0, spec); + assert(s->monitor_source); + free(n); + s->monitor_source->monitor_of = s; + + s->volume = PA_VOLUME_NORM; + + s->notify = NULL; + s->get_latency = NULL; + s->userdata = NULL; + + r = pa_idxset_put(core->sinks, s, &s->index); + assert(s->index != PA_IDXSET_INVALID && r >= 0); + + pa_sample_snprint(st, sizeof(st), spec); + fprintf(stderr, "sink: created %u \"%s\" with sample spec \"%s\"\n", s->index, s->name, st); + + return s; +} + +void pa_sink_free(struct pa_sink *s) { + struct pa_sink_input *i, *j = NULL; + assert(s); + + pa_namereg_unregister(s->core, s->name); + + while ((i = pa_idxset_first(s->inputs, NULL))) { + assert(i != j); + pa_sink_input_kill(i); + j = i; + } + pa_idxset_free(s->inputs, NULL, NULL); + + pa_source_free(s->monitor_source); + pa_idxset_remove_by_data(s->core->sinks, s, NULL); + + fprintf(stderr, "sink: freed %u \"%s\"\n", s->index, s->name); + + free(s->name); + free(s->description); + free(s); +} + +void pa_sink_notify(struct pa_sink*s) { + assert(s); + + if (s->notify) + s->notify(s); +} + +static unsigned fill_mix_info(struct pa_sink *s, struct pa_mix_info *info, unsigned maxinfo) { + uint32_t index = PA_IDXSET_INVALID; + struct pa_sink_input *i; + unsigned n = 0; + + assert(s && info); + + for (i = pa_idxset_first(s->inputs, &index); maxinfo > 0 && i; i = pa_idxset_next(s->inputs, &index)) { + if (pa_sink_input_peek(i, &info->chunk) < 0) + continue; + + info->volume = i->volume; + + assert(info->chunk.memblock && info->chunk.memblock->data && info->chunk.length); + info->userdata = i; + + info++; + maxinfo--; + n++; + } + + return n; +} + +static void inputs_drop(struct pa_sink *s, struct pa_mix_info *info, unsigned maxinfo, size_t length) { + assert(s && info); + + for (; maxinfo > 0; maxinfo--, info++) { + struct pa_sink_input *i = info->userdata; + assert(i && info->chunk.memblock); + + pa_memblock_unref(info->chunk.memblock); + pa_sink_input_drop(i, length); + } +} + +int pa_sink_render(struct pa_sink*s, size_t length, struct pa_memchunk *result) { + struct pa_mix_info info[MAX_MIX_CHANNELS]; + unsigned n; + size_t l; + assert(s && length && result); + + n = fill_mix_info(s, info, MAX_MIX_CHANNELS); + + if (n <= 0) + return -1; + + if (n == 1) { + uint32_t volume = PA_VOLUME_NORM; + struct pa_sink_input *i = info[0].userdata; + assert(i); + *result = info[0].chunk; + pa_memblock_ref(result->memblock); + + if (result->length > length) + result->length = length; + + l = result->length; + + if (s->volume != PA_VOLUME_NORM || info[0].volume != PA_VOLUME_NORM) + volume = pa_volume_multiply(s->volume, info[0].volume); + + if (volume != PA_VOLUME_NORM) { + pa_memchunk_make_writable(result); + pa_volume_memchunk(result, &s->sample_spec, volume); + } + } else { + result->memblock = pa_memblock_new(length); + assert(result->memblock); + + result->length = l = pa_mix(info, n, result->memblock->data, length, &s->sample_spec, s->volume); + result->index = 0; + + assert(l); + } + + inputs_drop(s, info, n, l); + + assert(s->monitor_source); + pa_source_post(s->monitor_source, result); + + return 0; +} + +int pa_sink_render_into(struct pa_sink*s, struct pa_memchunk *target) { + struct pa_mix_info info[MAX_MIX_CHANNELS]; + unsigned n; + size_t l; + assert(s && target && target->length && target->memblock && target->memblock->data); + + n = fill_mix_info(s, info, MAX_MIX_CHANNELS); + + if (n <= 0) + return -1; + + if (n == 1) { + uint32_t volume = PA_VOLUME_NORM; + struct pa_sink_info *i = info[0].userdata; + assert(i); + + l = target->length; + if (l > info[0].chunk.length) + l = info[0].chunk.length; + + memcpy(target->memblock->data+target->index, info[0].chunk.memblock->data + info[0].chunk.index, l); + target->length = l; + + if (s->volume != PA_VOLUME_NORM || info[0].volume != PA_VOLUME_NORM) + volume = pa_volume_multiply(s->volume, info[0].volume); + + if (volume != PA_VOLUME_NORM) + pa_volume_memchunk(target, &s->sample_spec, volume); + } else + target->length = l = pa_mix(info, n, target->memblock->data+target->index, target->length, &s->sample_spec, s->volume); + + assert(l); + inputs_drop(s, info, n, l); + + assert(s->monitor_source); + pa_source_post(s->monitor_source, target); + + return 0; +} + +void pa_sink_render_into_full(struct pa_sink *s, struct pa_memchunk *target) { + struct pa_memchunk chunk; + size_t l, d; + assert(s && target && target->memblock && target->length && target->memblock->data); + + l = target->length; + d = 0; + while (l > 0) { + chunk = *target; + chunk.index += d; + chunk.length -= d; + + if (pa_sink_render_into(s, &chunk) < 0) + break; + + d += chunk.length; + l -= chunk.length; + } + + if (l > 0) { + chunk = *target; + chunk.index += d; + chunk.length -= d; + pa_silence_memchunk(&chunk, &s->sample_spec); + } +} + +uint32_t pa_sink_get_latency(struct pa_sink *s) { + assert(s); + + if (!s->get_latency) + return 0; + + return s->get_latency(s); +} + +struct pa_sink* pa_sink_get_default(struct pa_core *c) { + struct pa_sink *sink; + assert(c); + + if ((sink = pa_idxset_get_by_index(c->sinks, c->default_sink_index))) + return sink; + + if (!(sink = pa_idxset_first(c->sinks, &c->default_sink_index))) + return NULL; + + fprintf(stderr, "core: default sink vanished, setting to %u.\n", sink->index); + return sink; +} + +void pa_sink_set_owner(struct pa_sink *sink, struct pa_module *m) { + sink->owner = m; + + if (sink->monitor_source) + pa_source_set_owner(sink->monitor_source, m); +} diff --git a/polyp/sink.h b/polyp/sink.h new file mode 100644 index 00000000..2b5d9495 --- /dev/null +++ b/polyp/sink.h @@ -0,0 +1,67 @@ +#ifndef foosinkhfoo +#define foosinkhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +struct pa_sink; + +#include + +#include "core.h" +#include "sample.h" +#include "idxset.h" +#include "source.h" + +struct pa_sink { + uint32_t index; + + char *name, *description; + struct pa_module *owner; + struct pa_core *core; + struct pa_sample_spec sample_spec; + struct pa_idxset *inputs; + + struct pa_source *monitor_source; + + uint32_t volume; + + void (*notify)(struct pa_sink*sink); + uint32_t (*get_latency)(struct pa_sink *s); + void *userdata; +}; + +struct pa_sink* pa_sink_new(struct pa_core *core, const char *name, int fail, const struct pa_sample_spec *spec); +void pa_sink_free(struct pa_sink* s); + +int pa_sink_render(struct pa_sink*s, size_t length, struct pa_memchunk *result); +int pa_sink_render_into(struct pa_sink*s, struct pa_memchunk *target); +void pa_sink_render_into_full(struct pa_sink *s, struct pa_memchunk *target); + +uint32_t pa_sink_get_latency(struct pa_sink *s); + +void pa_sink_notify(struct pa_sink*s); + +struct pa_sink* pa_sink_get_default(struct pa_core *c); + +void pa_sink_set_owner(struct pa_sink *sink, struct pa_module *m); + +#endif diff --git a/polyp/sioman.c b/polyp/sioman.c new file mode 100644 index 00000000..999b8a5c --- /dev/null +++ b/polyp/sioman.c @@ -0,0 +1,42 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#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 new file mode 100644 index 00000000..1b60d4a9 --- /dev/null +++ b/polyp/sioman.h @@ -0,0 +1,28 @@ +#ifndef foosiomanhfoo +#define foosiomanhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +int pa_stdio_acquire(void); +void pa_stdio_release(void); + +#endif diff --git a/polyp/socket-client.c b/polyp/socket-client.c new file mode 100644 index 00000000..a2187e6a --- /dev/null +++ b/polyp/socket-client.c @@ -0,0 +1,236 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "socket-client.h" +#include "socket-util.h" +#include "util.h" + +struct pa_socket_client { + struct pa_mainloop_api *mainloop; + int fd; + + void *io_source, *fixed_source; + void (*callback)(struct pa_socket_client*c, struct pa_iochannel *io, void *userdata); + void *userdata; +}; + +static struct pa_socket_client*pa_socket_client_new(struct pa_mainloop_api *m) { + struct pa_socket_client *c; + assert(m); + + c = malloc(sizeof(struct pa_socket_client)); + assert(c); + c->mainloop = m; + c->fd = -1; + c->io_source = c->fixed_source = NULL; + c->callback = NULL; + c->userdata = NULL; + return c; +} + +static void do_call(struct pa_socket_client *c) { + struct pa_iochannel *io; + int error, lerror; + assert(c && c->callback); + + lerror = sizeof(error); + if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &error, &lerror) < 0) { + fprintf(stderr, "getsockopt(): %s\n", strerror(errno)); + goto failed; + } + + if (lerror != sizeof(error)) { + fprintf(stderr, "getsocktop() returned invalid size.\n"); + goto failed; + } + + if (error != 0) { + fprintf(stderr, "connect(): %s\n", strerror(error)); + goto failed; + } + + io = pa_iochannel_new(c->mainloop, c->fd, c->fd); + assert(io); + c->fd = -1; + c->callback(c, io, c->userdata); + + return; + +failed: + close(c->fd); + c->fd = -1; + c->callback(c, NULL, c->userdata); + return; +} + +static void connect_fixed_cb(struct pa_mainloop_api *m, void *id, void *userdata) { + struct pa_socket_client *c = userdata; + assert(m && c && c->fixed_source == id); + m->cancel_fixed(m, c->fixed_source); + c->fixed_source = NULL; + do_call(c); +} + +static void connect_io_cb(struct pa_mainloop_api*m, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { + struct pa_socket_client *c = userdata; + assert(m && c && c->io_source == id && fd >= 0); + m->cancel_io(m, c->io_source); + c->io_source = NULL; + do_call(c); +} + +static int do_connect(struct pa_socket_client *c, const struct sockaddr *sa, socklen_t len) { + int r; + assert(c && sa && len); + + pa_make_nonblock_fd(c->fd); + + if ((r = connect(c->fd, sa, len)) < 0) { + if (errno != EINPROGRESS) { + fprintf(stderr, "connect(): %s\n", strerror(errno)); + return -1; + } + + c->io_source = c->mainloop->source_io(c->mainloop, c->fd, PA_MAINLOOP_API_IO_EVENT_OUTPUT, connect_io_cb, c); + assert(c->io_source); + } else { + c->fixed_source = c->mainloop->source_fixed(c->mainloop, connect_fixed_cb, c); + assert(c->fixed_source); + } + + return 0; +} + +struct pa_socket_client* pa_socket_client_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port) { + struct pa_socket_client *c; + struct sockaddr_in sa; + assert(m && address && port); + + c = pa_socket_client_new(m); + assert(c); + + if ((c->fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + fprintf(stderr, "socket(): %s\n", strerror(errno)); + goto fail; + } + + pa_socket_tcp_low_delay(c->fd); + + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + sa.sin_addr.s_addr = htonl(address); + + if (do_connect(c, (struct sockaddr*) &sa, sizeof(sa)) < 0) + goto fail; + + return c; + +fail: + pa_socket_client_free(c); + return NULL; +} + +struct pa_socket_client* pa_socket_client_new_unix(struct pa_mainloop_api *m, const char *filename) { + struct pa_socket_client *c; + struct sockaddr_un sa; + assert(m && filename); + + c = pa_socket_client_new(m); + assert(c); + + if ((c->fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { + fprintf(stderr, "socket(): %s\n", strerror(errno)); + goto fail; + } + + pa_socket_low_delay(c->fd); + + sa.sun_family = AF_LOCAL; + strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1); + sa.sun_path[sizeof(sa.sun_path) - 1] = 0; + + if (do_connect(c, (struct sockaddr*) &sa, sizeof(sa)) < 0) + goto fail; + + return c; + +fail: + pa_socket_client_free(c); + return NULL; +} + +struct pa_socket_client* pa_socket_client_new_sockaddr(struct pa_mainloop_api *m, const struct sockaddr *sa, size_t salen) { + struct pa_socket_client *c; + assert(m && sa); + c = pa_socket_client_new(m); + assert(c); + + if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) { + fprintf(stderr, "socket(): %s\n", strerror(errno)); + goto fail; + } + + if (sa->sa_family == AF_INET) + pa_socket_tcp_low_delay(c->fd); + else + pa_socket_low_delay(c->fd); + + if (do_connect(c, sa, salen) < 0) + goto fail; + + return c; + +fail: + pa_socket_client_free(c); + return NULL; + +} + +void pa_socket_client_free(struct pa_socket_client *c) { + assert(c && c->mainloop); + if (c->io_source) + c->mainloop->cancel_io(c->mainloop, c->io_source); + if (c->fixed_source) + c->mainloop->cancel_fixed(c->mainloop, c->fixed_source); + if (c->fd >= 0) + close(c->fd); + free(c); +} + +void pa_socket_client_set_callback(struct pa_socket_client *c, void (*on_connection)(struct pa_socket_client *c, struct pa_iochannel*io, void *userdata), void *userdata) { + assert(c); + c->callback = on_connection; + c->userdata = userdata; +} diff --git a/polyp/socket-client.h b/polyp/socket-client.h new file mode 100644 index 00000000..2a89210e --- /dev/null +++ b/polyp/socket-client.h @@ -0,0 +1,41 @@ +#ifndef foosocketclienthfoo +#define foosocketclienthfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include "mainloop-api.h" +#include "iochannel.h" + +/* It is safe to destroy the calling socket_client object from the callback */ + +struct pa_socket_client; + +struct pa_socket_client* pa_socket_client_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port); +struct pa_socket_client* pa_socket_client_new_unix(struct pa_mainloop_api *m, const char *filename); +struct pa_socket_client* pa_socket_client_new_sockaddr(struct pa_mainloop_api *m, const struct sockaddr *sa, size_t salen); + +void pa_socket_client_free(struct pa_socket_client *c); + +void pa_socket_client_set_callback(struct pa_socket_client *c, void (*on_connection)(struct pa_socket_client *c, struct pa_iochannel*io, void *userdata), void *userdata); + +#endif diff --git a/polyp/socket-server.c b/polyp/socket-server.c new file mode 100644 index 00000000..0f497377 --- /dev/null +++ b/polyp/socket-server.c @@ -0,0 +1,209 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "socket-server.h" +#include "socket-util.h" + +struct pa_socket_server { + int fd; + char *filename; + + void (*on_connection)(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata); + void *userdata; + + void *mainloop_source; + struct pa_mainloop_api *mainloop; + enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX } type; +}; + +static void callback(struct pa_mainloop_api *mainloop, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { + struct pa_socket_server *s = userdata; + struct pa_iochannel *io; + int nfd; + assert(s && s->mainloop == mainloop && s->mainloop_source == id && id && fd >= 0 && fd == s->fd && events == PA_MAINLOOP_API_IO_EVENT_INPUT); + + if ((nfd = accept(fd, NULL, NULL)) < 0) { + fprintf(stderr, "accept(): %s\n", strerror(errno)); + return; + } + + if (!s->on_connection) { + close(nfd); + return; + } + + /* There should be a check for socket type here */ + if (s->type == SOCKET_SERVER_IPV4) + pa_socket_tcp_low_delay(fd); + else + pa_socket_low_delay(fd); + + io = pa_iochannel_new(s->mainloop, nfd, nfd); + assert(io); + s->on_connection(s, io, s->userdata); +} + +struct pa_socket_server* pa_socket_server_new(struct pa_mainloop_api *m, int fd) { + struct pa_socket_server *s; + assert(m && fd >= 0); + + s = malloc(sizeof(struct pa_socket_server)); + assert(s); + s->fd = fd; + s->filename = NULL; + s->on_connection = NULL; + s->userdata = NULL; + + s->mainloop = m; + s->mainloop_source = m->source_io(m, fd, PA_MAINLOOP_API_IO_EVENT_INPUT, callback, s); + assert(s->mainloop_source); + + s->type = SOCKET_SERVER_GENERIC; + + return s; +} + +struct pa_socket_server* pa_socket_server_new_unix(struct pa_mainloop_api *m, const char *filename) { + int fd = -1; + struct sockaddr_un sa; + struct pa_socket_server *s; + + assert(m && filename); + + if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { + fprintf(stderr, "socket(): %s\n", strerror(errno)); + goto fail; + } + + sa.sun_family = AF_LOCAL; + strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1); + sa.sun_path[sizeof(sa.sun_path) - 1] = 0; + + pa_socket_low_delay(fd); + + if (bind(fd, (struct sockaddr*) &sa, SUN_LEN(&sa)) < 0) { + fprintf(stderr, "bind(): %s\n", strerror(errno)); + goto fail; + } + + if (listen(fd, 5) < 0) { + fprintf(stderr, "listen(): %s\n", strerror(errno)); + goto fail; + } + + s = pa_socket_server_new(m, fd); + assert(s); + + s->filename = strdup(filename); + assert(s->filename); + + s->type = SOCKET_SERVER_UNIX; + + return s; + +fail: + if (fd >= 0) + close(fd); + + return NULL; +} + +struct pa_socket_server* pa_socket_server_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port) { + struct pa_socket_server *ss; + int fd = -1; + struct sockaddr_in sa; + int on = 1; + + assert(m && port); + + if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + fprintf(stderr, "socket(): %s\n", strerror(errno)); + goto fail; + } + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) + fprintf(stderr, "setsockopt(): %s\n", strerror(errno)); + + pa_socket_tcp_low_delay(fd); + + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + sa.sin_addr.s_addr = htonl(address); + + if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { + fprintf(stderr, "bind(): %s\n", strerror(errno)); + goto fail; + } + + if (listen(fd, 5) < 0) { + fprintf(stderr, "listen(): %s\n", strerror(errno)); + goto fail; + } + + if ((ss = pa_socket_server_new(m, fd))) + ss->type = SOCKET_SERVER_IPV4; + + return ss; + +fail: + if (fd >= 0) + close(fd); + + return NULL; +} + +void pa_socket_server_free(struct pa_socket_server*s) { + assert(s); + close(s->fd); + + if (s->filename) { + unlink(s->filename); + free(s->filename); + } + + + s->mainloop->cancel_io(s->mainloop, s->mainloop_source); + + free(s); +} + +void pa_socket_server_set_callback(struct pa_socket_server*s, void (*on_connection)(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata), void *userdata) { + assert(s); + + s->on_connection = on_connection; + s->userdata = userdata; +} diff --git a/polyp/socket-server.h b/polyp/socket-server.h new file mode 100644 index 00000000..6661a66e --- /dev/null +++ b/polyp/socket-server.h @@ -0,0 +1,41 @@ +#ifndef foosocketserverhfoo +#define foosocketserverhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include "mainloop-api.h" +#include "iochannel.h" + +/* It is safe to destroy the calling socket_server object from the callback */ + +struct pa_socket_server; + +struct pa_socket_server* pa_socket_server_new(struct pa_mainloop_api *m, int fd); +struct pa_socket_server* pa_socket_server_new_unix(struct pa_mainloop_api *m, const char *filename); +struct pa_socket_server* pa_socket_server_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port); + +void pa_socket_server_free(struct pa_socket_server*s); + +void pa_socket_server_set_callback(struct pa_socket_server*s, void (*on_connection)(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata), void *userdata); + +#endif diff --git a/polyp/socket-util.c b/polyp/socket-util.c new file mode 100644 index 00000000..e0a3c28d --- /dev/null +++ b/polyp/socket-util.c @@ -0,0 +1,216 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "socket-util.h" +#include "util.h" + +void pa_socket_peer_to_string(int fd, char *c, size_t l) { + struct stat st; + + assert(c && l && fd >= 0); + + if (fstat(fd, &st) < 0) { + snprintf(c, l, "Invalid client fd"); + return; + } + + if (S_ISSOCK(st.st_mode)) { + union { + struct sockaddr sa; + struct sockaddr_in in; + struct sockaddr_un un; + } sa; + socklen_t sa_len = sizeof(sa); + + if (getpeername(fd, &sa.sa, &sa_len) >= 0) { + + if (sa.sa.sa_family == AF_INET) { + uint32_t ip = ntohl(sa.in.sin_addr.s_addr); + + snprintf(c, l, "TCP/IP client from %i.%i.%i.%i:%u", + ip >> 24, + (ip >> 16) & 0xFF, + (ip >> 8) & 0xFF, + ip & 0xFF, + ntohs(sa.in.sin_port)); + return; + } else if (sa.sa.sa_family == AF_LOCAL) { + snprintf(c, l, "UNIX socket client"); + return; + } + + } + snprintf(c, l, "Unknown network client"); + return; + } else if (S_ISCHR(st.st_mode) && (fd == 0 || fd == 1)) { + snprintf(c, l, "STDIN/STDOUT client"); + return; + } + + snprintf(c, l, "Unknown client"); +} + +int pa_socket_low_delay(int fd) { + int priority; + assert(fd >= 0); + + priority = 7; + if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) + return -1; + + return 0; +} + +int pa_socket_tcp_low_delay(int fd) { + int ret, tos; + + assert(fd >= 0); + + ret = pa_socket_low_delay(fd); + +/* on = 1; */ +/* if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) */ +/* ret = -1; */ + + tos = IPTOS_LOWDELAY; + if (setsockopt(fd, SOL_IP, IP_TOS, &tos, sizeof(tos)) < 0) + ret = -1; + + return ret; + +} + +int pa_socket_set_rcvbuf(int fd, size_t l) { + assert(fd >= 0); + + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &l, sizeof(l)) < 0) + return -1; + + return 0; +} + +int pa_socket_set_sndbuf(int fd, size_t l) { + assert(fd >= 0); + + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &l, sizeof(l)) < 0) + return -1; + + return 0; +} + +int pa_unix_socket_is_stale(const char *fn) { + struct sockaddr_un sa; + int fd = -1, ret = -1; + + if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { + fprintf(stderr, "socket(): %s\n", strerror(errno)); + goto finish; + } + + sa.sun_family = AF_LOCAL; + strncpy(sa.sun_path, fn, sizeof(sa.sun_path)-1); + sa.sun_path[sizeof(sa.sun_path) - 1] = 0; + + if (connect(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) { + if (errno == ECONNREFUSED) + ret = 1; + } else + ret = 0; + +finish: + if (fd >= 0) + close(fd); + + return ret; +} + +int pa_unix_socket_remove_stale(const char *fn) { + int r; + + if ((r = pa_unix_socket_is_stale(fn)) < 0) + return errno != ENOENT ? -1 : 0; + + if (!r) + return 0; + + /* Yes, here is a race condition. But who cares? */ + if (unlink(fn) < 0) + return -1; + + return 0; +} + +int pa_unix_socket_make_secure_dir(const char *fn) { + int ret = -1; + char *slash, *dir = strdup(fn); + assert(dir); + + if (!(slash = strrchr(dir, '/'))) + goto finish; + *slash = 0; + + if (pa_make_secure_dir(dir) < 0) + goto finish; + + ret = 0; + +finish: + free(dir); + return ret; +} + +int pa_unix_socket_remove_secure_dir(const char *fn) { + int ret = -1; + char *slash, *dir = strdup(fn); + assert(dir); + + if (!(slash = strrchr(dir, '/'))) + goto finish; + *slash = 0; + + if (rmdir(dir) < 0) + goto finish; + + ret = 0; + +finish: + free(dir); + return ret; +} diff --git a/polyp/socket-util.h b/polyp/socket-util.h new file mode 100644 index 00000000..85133feb --- /dev/null +++ b/polyp/socket-util.h @@ -0,0 +1,41 @@ +#ifndef foosocketutilhfoo +#define foosocketutilhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +void pa_socket_peer_to_string(int fd, char *c, size_t l); + +int pa_socket_low_delay(int fd); +int pa_socket_tcp_low_delay(int fd); + +int pa_socket_set_sndbuf(int fd, size_t l); +int pa_socket_set_rcvbuf(int fd, size_t l); + +int pa_unix_socket_is_stale(const char *fn); +int pa_unix_socket_remove_stale(const char *fn); + +int pa_unix_socket_make_secure_dir(const char *fn); +int pa_unix_socket_remove_secure_dir(const char *fn); + +#endif diff --git a/polyp/source-output.c b/polyp/source-output.c new file mode 100644 index 00000000..2705fdb3 --- /dev/null +++ b/polyp/source-output.c @@ -0,0 +1,101 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "source-output.h" + +struct pa_source_output* pa_source_output_new(struct pa_source *s, const char *name, const struct pa_sample_spec *spec) { + struct pa_source_output *o; + struct pa_resampler *resampler = NULL; + int r; + assert(s && spec); + + if (!pa_sample_spec_equal(&s->sample_spec, spec)) + if (!(resampler = pa_resampler_new(&s->sample_spec, spec))) + return NULL; + + o = malloc(sizeof(struct pa_source_output)); + assert(o); + o->name = name ? strdup(name) : NULL; + o->client = NULL; + o->owner = NULL; + o->source = s; + o->sample_spec = *spec; + + o->push = NULL; + o->kill = NULL; + o->userdata = NULL; + o->resampler = resampler; + + assert(s->core); + r = pa_idxset_put(s->core->source_outputs, o, &o->index); + assert(r == 0 && o->index != PA_IDXSET_INVALID); + r = pa_idxset_put(s->outputs, o, NULL); + assert(r == 0); + + return o; +} + +void pa_source_output_free(struct pa_source_output* o) { + assert(o); + + assert(o->source && o->source->core); + pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL); + pa_idxset_remove_by_data(o->source->outputs, o, NULL); + + if (o->resampler) + pa_resampler_free(o->resampler); + + free(o->name); + free(o); +} + +void pa_source_output_kill(struct pa_source_output*i) { + assert(i); + + if (i->kill) + i->kill(i); +} + +void pa_source_output_push(struct pa_source_output *o, const struct pa_memchunk *chunk) { + struct pa_memchunk rchunk; + assert(o && chunk && chunk->length && o->push); + + if (!o->resampler) { + o->push(o, chunk); + return; + } + + pa_resampler_run(o->resampler, chunk, &rchunk); + if (!rchunk.length) + return; + + assert(rchunk.memblock); + o->push(o, &rchunk); + pa_memblock_unref(rchunk.memblock); +} diff --git a/polyp/source-output.h b/polyp/source-output.h new file mode 100644 index 00000000..0e6e2cfd --- /dev/null +++ b/polyp/source-output.h @@ -0,0 +1,58 @@ +#ifndef foosourceoutputhfoo +#define foosourceoutputhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include "source.h" +#include "sample.h" +#include "memblockq.h" +#include "resampler.h" +#include "module.h" +#include "client.h" + +struct pa_source_output { + uint32_t index; + + char *name; + struct pa_module *owner; + struct pa_client *client; + struct pa_source *source; + struct pa_sample_spec sample_spec; + + void (*push)(struct pa_source_output *o, const struct pa_memchunk *chunk); + void (*kill)(struct pa_source_output* o); + + struct pa_resampler* resampler; + + void *userdata; +}; + +struct pa_source_output* pa_source_output_new(struct pa_source *s, const char *name, const struct pa_sample_spec *spec); +void pa_source_output_free(struct pa_source_output* o); + +void pa_source_output_kill(struct pa_source_output*o); + +void pa_source_output_push(struct pa_source_output *o, const struct pa_memchunk *chunk); + +#endif diff --git a/polyp/source.c b/polyp/source.c new file mode 100644 index 00000000..ccde0e2f --- /dev/null +++ b/polyp/source.c @@ -0,0 +1,131 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "source.h" +#include "source-output.h" +#include "namereg.h" + +struct pa_source* pa_source_new(struct pa_core *core, const char *name, int fail, const struct pa_sample_spec *spec) { + struct pa_source *s; + char st[256]; + int r; + assert(core && spec && name); + + s = malloc(sizeof(struct pa_source)); + assert(s); + + if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SOURCE, s, fail))) { + free(s); + return NULL; + } + + s->name = strdup(name); + s->description = NULL; + + s->owner = NULL; + s->core = core; + s->sample_spec = *spec; + s->outputs = pa_idxset_new(NULL, NULL); + s->monitor_of = NULL; + + s->notify = NULL; + s->userdata = NULL; + + r = pa_idxset_put(core->sources, s, &s->index); + assert(s->index != PA_IDXSET_INVALID && r >= 0); + + pa_sample_snprint(st, sizeof(st), spec); + fprintf(stderr, "source: created %u \"%s\" with sample spec \"%s\"\n", s->index, s->name, st); + + return s; +} + +void pa_source_free(struct pa_source *s) { + struct pa_source_output *o, *j = NULL; + assert(s); + + pa_namereg_unregister(s->core, s->name); + + while ((o = pa_idxset_first(s->outputs, NULL))) { + assert(o != j); + pa_source_output_kill(o); + j = o; + } + pa_idxset_free(s->outputs, NULL, NULL); + + pa_idxset_remove_by_data(s->core->sources, s, NULL); + + fprintf(stderr, "source: freed %u \"%s\"\n", s->index, s->name); + + free(s->name); + free(s->description); + free(s); +} + +void pa_source_notify(struct pa_source*s) { + assert(s); + + if (s->notify) + s->notify(s); +} + +static int do_post(void *p, uint32_t index, int *del, void*userdata) { + struct pa_memchunk *chunk = userdata; + struct pa_source_output *o = p; + assert(o && o->push && del && chunk); + + pa_source_output_push(o, chunk); + return 0; +} + +void pa_source_post(struct pa_source*s, struct pa_memchunk *chunk) { + assert(s && chunk); + + pa_idxset_foreach(s->outputs, do_post, chunk); +} + +struct pa_source* pa_source_get_default(struct pa_core *c) { + struct pa_source *source; + assert(c); + + if ((source = pa_idxset_get_by_index(c->sources, c->default_source_index))) + return source; + + if (!(source = pa_idxset_first(c->sources, &c->default_source_index))) + return NULL; + + fprintf(stderr, "core: default source vanished, setting to %u.\n", source->index); + return source; +} + +void pa_source_set_owner(struct pa_source *s, struct pa_module *m) { + assert(s); + s->owner = m; +} diff --git a/polyp/source.h b/polyp/source.h new file mode 100644 index 00000000..9c584a6d --- /dev/null +++ b/polyp/source.h @@ -0,0 +1,61 @@ +#ifndef foosourcehfoo +#define foosourcehfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +struct pa_source; + +#include +#include "core.h" +#include "sample.h" +#include "idxset.h" +#include "memblock.h" +#include "memchunk.h" +#include "sink.h" + +struct pa_source { + uint32_t index; + + char *name, *description; + struct pa_module *owner; + struct pa_core *core; + struct pa_sample_spec sample_spec; + struct pa_idxset *outputs; + struct pa_sink *monitor_of; + + void (*notify)(struct pa_source*source); + void *userdata; +}; + +struct pa_source* pa_source_new(struct pa_core *core, const char *name, int fail, const struct pa_sample_spec *spec); +void pa_source_free(struct pa_source *s); + +/* Pass a new memory block to all output streams */ +void pa_source_post(struct pa_source*s, struct pa_memchunk *b); + +void pa_source_notify(struct pa_source *s); + +struct pa_source* pa_source_get_default(struct pa_core *c); + +void pa_source_set_owner(struct pa_source *s, struct pa_module *m); + +#endif diff --git a/polyp/strbuf.c b/polyp/strbuf.c new file mode 100644 index 00000000..c6a3772d --- /dev/null +++ b/polyp/strbuf.c @@ -0,0 +1,164 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "strbuf.h" + +struct chunk { + struct chunk *next; + size_t length; + char text[]; +}; + +struct pa_strbuf { + size_t length; + struct chunk *head, *tail; +}; + +struct pa_strbuf *pa_strbuf_new(void) { + struct pa_strbuf *sb = malloc(sizeof(struct pa_strbuf)); + assert(sb); + sb->length = 0; + sb->head = sb->tail = NULL; + return sb; +} + +void pa_strbuf_free(struct pa_strbuf *sb) { + assert(sb); + while (sb->head) { + struct chunk *c = sb->head; + sb->head = sb->head->next; + free(c); + } + + free(sb); +} + +char *pa_strbuf_tostring(struct pa_strbuf *sb) { + char *t, *e; + struct chunk *c; + assert(sb); + + t = malloc(sb->length+1); + assert(t); + + e = t; + for (c = sb->head; c; c = c->next) { + memcpy(e, c->text, c->length); + e += c->length; + } + + *e = 0; + + return t; +} + +char *pa_strbuf_tostring_free(struct pa_strbuf *sb) { + char *t; + assert(sb); + t = pa_strbuf_tostring(sb); + pa_strbuf_free(sb); + return t; +} + +void pa_strbuf_puts(struct pa_strbuf *sb, const char *t) { + assert(sb && t); + pa_strbuf_putsn(sb, t, strlen(t)); +} + +void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t l) { + struct chunk *c; + assert(sb && t); + + if (!l) + return; + + c = malloc(sizeof(struct chunk)+l); + assert(c); + + c->next = NULL; + c->length = l; + memcpy(c->text, t, l); + + if (sb->tail) { + assert(sb->head); + sb->tail->next = c; + } else { + assert(!sb->head); + sb->head = c; + } + + sb->tail = c; + sb->length += l; +} + +/* The following is based on an example from the GNU libc documentation */ + +int pa_strbuf_printf(struct pa_strbuf *sb, const char *format, ...) { + int r, size = 100; + struct chunk *c = NULL; + + assert(sb); + + for(;;) { + va_list ap; + + c = realloc(c, sizeof(struct chunk)+size); + assert(c); + + va_start(ap, format); + r = vsnprintf(c->text, size, format, ap); + va_end(ap); + + if (r > -1 && r < size) { + c->length = r; + c->next = NULL; + + if (sb->tail) { + assert(sb->head); + sb->tail->next = c; + } else { + assert(!sb->head); + sb->head = c; + } + + sb->tail = c; + sb->length += r; + + return r; + } + + if (r > -1) /* glibc 2.1 */ + size = r+1; + else /* glibc 2.0 */ + size *= 2; + } +} diff --git a/polyp/strbuf.h b/polyp/strbuf.h new file mode 100644 index 00000000..d672c42a --- /dev/null +++ b/polyp/strbuf.h @@ -0,0 +1,36 @@ +#ifndef foostrbufhfoo +#define foostrbufhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +struct pa_strbuf; + +struct pa_strbuf *pa_strbuf_new(void); +void pa_strbuf_free(struct pa_strbuf *sb); +char *pa_strbuf_tostring(struct pa_strbuf *sb); +char *pa_strbuf_tostring_free(struct pa_strbuf *sb); + +int pa_strbuf_printf(struct pa_strbuf *sb, const char *format, ...) __attribute__ ((format (printf, 2, 3)));; +void pa_strbuf_puts(struct pa_strbuf *sb, const char *t); +void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t m); + +#endif diff --git a/polyp/tagstruct.c b/polyp/tagstruct.c new file mode 100644 index 00000000..0d93c1e9 --- /dev/null +++ b/polyp/tagstruct.c @@ -0,0 +1,241 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "tagstruct.h" + +enum tags { + TAG_STRING = 't', + TAG_U32 = 'L', + TAG_S32 = 'l', + TAG_U16 = 'S', + TAG_S16 = 's', + TAG_U8 = 'B', + TAG_S8 = 'b', + TAG_SAMPLE_SPEC = 'a', + TAG_ARBITRARY = 'x' +}; + +struct pa_tagstruct { + uint8_t *data; + size_t length, allocated; + size_t rindex; + + int dynamic; +}; + +struct pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) { + struct pa_tagstruct*t; + + assert(!data || (data && length)); + + t = malloc(sizeof(struct pa_tagstruct)); + assert(t); + t->data = (uint8_t*) data; + t->allocated = t->length = data ? length : 0; + t->rindex = 0; + t->dynamic = !data; + return t; +} + +void pa_tagstruct_free(struct pa_tagstruct*t) { + assert(t); + if (t->dynamic) + free(t->data); + free(t); +} + +uint8_t* pa_tagstruct_free_data(struct pa_tagstruct*t, size_t *l) { + uint8_t *p; + assert(t && t->dynamic && l); + p = t->data; + *l = t->length; + free(t); + return p; +} + +static void extend(struct pa_tagstruct*t, size_t l) { + assert(t && t->dynamic); + + if (l <= t->allocated) + return; + + t->data = realloc(t->data, t->allocated = l+100); + assert(t->data); +} + +void pa_tagstruct_puts(struct pa_tagstruct*t, const char *s) { + size_t l; + assert(t && s); + l = strlen(s)+2; + extend(t, l); + t->data[t->length] = TAG_STRING; + strcpy(t->data+t->length+1, s); + t->length += l; +} + +void pa_tagstruct_putu32(struct pa_tagstruct*t, uint32_t i) { + assert(t); + extend(t, 5); + t->data[t->length] = TAG_U32; + *((uint32_t*) (t->data+t->length+1)) = htonl(i); + t->length += 5; +} + +void pa_tagstruct_putu8(struct pa_tagstruct*t, uint8_t c) { + assert(t); + extend(t, 2); + t->data[t->length] = TAG_U8; + *(t->data+t->length+1) = c; + t->length += 2; +} + +void pa_tagstruct_put_sample_spec(struct pa_tagstruct *t, const struct pa_sample_spec *ss) { + assert(t && ss); + extend(t, 7); + t->data[t->length] = TAG_SAMPLE_SPEC; + t->data[t->length+1] = (uint8_t) ss->format; + t->data[t->length+2] = ss->channels; + *(uint32_t*) (t->data+t->length+3) = htonl(ss->rate); + t->length += 7; +} + + +void pa_tagstruct_put_arbitrary(struct pa_tagstruct *t, const void *p, size_t length) { + assert(t && p); + + extend(t, 5+length); + t->data[t->length] = TAG_ARBITRARY; + *((uint32_t*) (t->data+t->length+1)) = htonl(length); + if (length) + memcpy(t->data+t->length+5, p, length); + t->length += 5+length; +} + +int pa_tagstruct_gets(struct pa_tagstruct*t, const char **s) { + int error = 0; + size_t n; + char *c; + assert(t && s); + + if (t->rindex+2 > t->length) + return -1; + + if (t->data[t->rindex] != TAG_STRING) + return -1; + + error = 1; + for (n = 0, c = (char*) (t->data+t->rindex+1); t->rindex+1+n < t->length; n++, c++) + if (!*c) { + error = 0; + break; + } + + if (error) + return -1; + + *s = (char*) (t->data+t->rindex+1); + + t->rindex += n+2; + return 0; +} + +int pa_tagstruct_getu32(struct pa_tagstruct*t, uint32_t *i) { + assert(t && i); + + if (t->rindex+5 > t->length) + return -1; + + if (t->data[t->rindex] != TAG_U32) + return -1; + + *i = ntohl(*((uint32_t*) (t->data+t->rindex+1))); + t->rindex += 5; + return 0; +} + +int pa_tagstruct_getu8(struct pa_tagstruct*t, uint8_t *c) { + assert(t && c); + + if (t->rindex+2 > t->length) + return -1; + + if (t->data[t->rindex] != TAG_U8) + return -1; + + *c = t->data[t->rindex+1]; + t->rindex +=2; + return 0; +} + +int pa_tagstruct_get_sample_spec(struct pa_tagstruct *t, struct pa_sample_spec *ss) { + assert(t && ss); + + if (t->rindex+7 > t->length) + return -1; + + if (t->data[t->rindex] != TAG_SAMPLE_SPEC) + return -1; + + ss->format = t->data[t->rindex+1]; + ss->channels = t->data[t->rindex+2]; + ss->rate = ntohl(*(uint32_t*) (t->data+t->rindex+3)); + + t->rindex += 7; + return 0; +} + +int pa_tagstruct_get_arbitrary(struct pa_tagstruct *t, const void **p, size_t length) { + assert(t && p); + + if (t->rindex+5+length > t->length) + return -1; + + if (t->data[t->rindex] != TAG_ARBITRARY) + return -1; + + if (ntohl(*((uint32_t*) (t->data+t->rindex+1))) != length) + return -1; + + *p = t->data+t->rindex+5; + t->rindex += 5+length; + return 0; +} + +int pa_tagstruct_eof(struct pa_tagstruct*t) { + assert(t); + return t->rindex >= t->length; +} + +const uint8_t* pa_tagstruct_data(struct pa_tagstruct*t, size_t *l) { + assert(t && t->dynamic && l); + *l = t->length; + return t->data; +} + diff --git a/polyp/tagstruct.h b/polyp/tagstruct.h new file mode 100644 index 00000000..aefd03c4 --- /dev/null +++ b/polyp/tagstruct.h @@ -0,0 +1,53 @@ +#ifndef footagstructhfoo +#define footagstructhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +#include "sample.h" + +struct pa_tagstruct; + +struct pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length); +void pa_tagstruct_free(struct pa_tagstruct*t); +uint8_t* pa_tagstruct_free_data(struct pa_tagstruct*t, size_t *l); + +void pa_tagstruct_puts(struct pa_tagstruct*t, const char *s); +void pa_tagstruct_putu32(struct pa_tagstruct*t, uint32_t i); +void pa_tagstruct_putu8(struct pa_tagstruct*t, uint8_t c); +void pa_tagstruct_put_sample_spec(struct pa_tagstruct *t, const struct pa_sample_spec *ss); +void pa_tagstruct_put_arbitrary(struct pa_tagstruct*t, const void *p, size_t length); + +int pa_tagstruct_gets(struct pa_tagstruct*t, const char **s); +int pa_tagstruct_getu32(struct pa_tagstruct*t, uint32_t *i); +int pa_tagstruct_getu8(struct pa_tagstruct*t, uint8_t *c); +int pa_tagstruct_get_sample_spec(struct pa_tagstruct *t, struct pa_sample_spec *ss); +int pa_tagstruct_get_arbitrary(struct pa_tagstruct *t, const void **p, size_t length); + +int pa_tagstruct_eof(struct pa_tagstruct*t); +const uint8_t* pa_tagstruct_data(struct pa_tagstruct*t, size_t *l); + + + +#endif diff --git a/polyp/tokenizer.c b/polyp/tokenizer.c new file mode 100644 index 00000000..c7f18d26 --- /dev/null +++ b/polyp/tokenizer.c @@ -0,0 +1,89 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "tokenizer.h" +#include "dynarray.h" + +struct pa_tokenizer { + struct pa_dynarray *dynarray; +}; + +static void token_free(void *p, void *userdata) { + free(p); +} + +static void parse(struct pa_dynarray*a, const char *s, unsigned args) { + int infty = 0; + const char delimiter[] = " \t\n\r"; + const char *p; + assert(a && s); + + if (args == 0) + infty = 1; + + p = s+strspn(s, delimiter); + while (*p && (infty || args >= 2)) { + size_t l = strcspn(p, delimiter); + char *n = strndup(p, l); + assert(n); + pa_dynarray_append(a, n); + p += l; + p += strspn(p, delimiter); + args--; + } + + if (args && *p) { + char *n = strdup(p); + assert(n); + pa_dynarray_append(a, n); + } +} + +struct pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args) { + struct pa_tokenizer *t; + + t = malloc(sizeof(struct pa_tokenizer)); + assert(t); + t->dynarray = pa_dynarray_new(); + assert(t->dynarray); + + parse(t->dynarray, s, args); + return t; +} + +void pa_tokenizer_free(struct pa_tokenizer *t) { + assert(t); + pa_dynarray_free(t->dynarray, token_free, NULL); + free(t); +} + +const char *pa_tokenizer_get(struct pa_tokenizer *t, unsigned i) { + assert(t); + return pa_dynarray_get(t->dynarray, i); +} diff --git a/polyp/tokenizer.h b/polyp/tokenizer.h new file mode 100644 index 00000000..7d1cf9b5 --- /dev/null +++ b/polyp/tokenizer.h @@ -0,0 +1,32 @@ +#ifndef footokenizerhfoo +#define footokenizerhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +struct pa_tokenizer; + +struct pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args); +void pa_tokenizer_free(struct pa_tokenizer *t); + +const char *pa_tokenizer_get(struct pa_tokenizer *t, unsigned i); + +#endif diff --git a/polyp/util.c b/polyp/util.c new file mode 100644 index 00000000..6e75c240 --- /dev/null +++ b/polyp/util.c @@ -0,0 +1,147 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" + +void pa_make_nonblock_fd(int fd) { + int v; + + if ((v = fcntl(fd, F_GETFL)) >= 0) + if (!(v & O_NONBLOCK)) + fcntl(fd, F_SETFL, v|O_NONBLOCK); +} + +int pa_make_secure_dir(const char* dir) { + struct stat st; + + if (mkdir(dir, 0700) < 0) + if (errno != EEXIST) + return -1; + + if (lstat(dir, &st) < 0) + goto fail; + + if (!S_ISDIR(st.st_mode) || (st.st_uid != getuid()) || ((st.st_mode & 0777) != 0700)) + goto fail; + + return 0; + +fail: + rmdir(dir); + return -1; +} + +ssize_t pa_loop_read(int fd, void*data, size_t size) { + ssize_t ret = 0; + assert(fd >= 0 && data && size); + + while (size > 0) { + ssize_t r; + + if ((r = read(fd, data, size)) < 0) + return r; + + if (r == 0) + break; + + ret += r; + data += r; + size -= r; + } + + return ret; +} + +ssize_t pa_loop_write(int fd, const void*data, size_t size) { + ssize_t ret = 0; + assert(fd >= 0 && data && size); + + while (size > 0) { + ssize_t r; + + if ((r = write(fd, data, size)) < 0) + return r; + + if (r == 0) + break; + + ret += r; + data += r; + size -= r; + } + + return ret; +} + +void pa_check_for_sigpipe(void) { + struct sigaction sa; + + if (sigaction(SIGPIPE, NULL, &sa) < 0) { + fprintf(stderr, __FILE__": sigaction() failed: %s\n", strerror(errno)); + return; + } + + if (sa.sa_handler == SIG_DFL) + fprintf(stderr, "polypaudio: WARNING: SIGPIPE is not trapped. This might cause malfunction!\n"); +} + +/* The following is based on an example from the GNU libc documentation */ +char *pa_sprintf_malloc(const char *format, ...) { + int size = 100; + char *c = NULL; + + assert(format); + + for(;;) { + int r; + va_list ap; + + c = realloc(c, size); + assert(c); + + va_start(ap, format); + r = vsnprintf(c, size, format, ap); + va_end(ap); + + if (r > -1 && r < size) + return c; + + if (r > -1) /* glibc 2.1 */ + size = r+1; + else /* glibc 2.0 */ + size *= 2; + } +} diff --git a/polyp/util.h b/polyp/util.h new file mode 100644 index 00000000..96fde11c --- /dev/null +++ b/polyp/util.h @@ -0,0 +1,38 @@ +#ifndef fooutilhfoo +#define fooutilhfoo + +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +void pa_make_nonblock_fd(int fd); + +int pa_make_secure_dir(const char* dir); + +ssize_t pa_loop_read(int fd, void*data, size_t size); +ssize_t pa_loop_write(int fd, const void*data, size_t size); + +void pa_check_for_sigpipe(void); + +char *pa_sprintf_malloc(const char *format, ...) __attribute__ ((format (printf, 1, 2))); + +#endif diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index c4da7342..00000000 --- a/src/Makefile.am +++ /dev/null @@ -1,363 +0,0 @@ -# $Id$ -# -# This file is part of polypaudio. -# -# polypaudio is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# polypaudio is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with polypaudio; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA. - -AM_CFLAGS=-ansi -D_GNU_SOURCE - -EXTRA_DIST = polypaudio.run depmod.py -bin_PROGRAMS = polypaudio pacat pactl -noinst_PROGRAMS = pacat-simple parec-simple - -pkginclude_HEADERS=polyplib.h \ - polyplib-def.h \ - polyplib-simple.h \ - polyplib-error.h \ - mainloop-api.h \ - mainloop.h \ - mainloop-signal.h \ - sample.h - -pkglib_LTLIBRARIES=libiochannel.la \ - libsocket-server.la \ - libsocket-client.la \ - libpstream.la \ - libpacket.la \ - liboss-util.la \ - libalsa-util.la \ - libioline.la \ - libcli.la \ - libprotocol-cli.la \ - libtagstruct.la \ - libpstream-util.la \ - libpdispatch.la \ - libauthkey.la \ - libsocket-util.la \ - libprotocol-simple.la \ - libprotocol-esound.la \ - libprotocol-native.la \ - module-cli.la \ - module-cli-protocol-tcp.la \ - module-cli-protocol-unix.la \ - module-pipe-sink.la \ - module-alsa-sink.la \ - module-alsa-source.la \ - module-oss.la \ - module-oss-mmap.la \ - module-simple-protocol-tcp.la \ - module-simple-protocol-unix.la \ - module-esound-protocol-tcp.la \ - module-esound-protocol-unix.la \ - module-native-protocol-tcp.la \ - module-native-protocol-unix.la - -lib_LTLIBRARIES=libpolyp.la \ - libpolyp-simple.la \ - libpolyp-error.la \ - libpolyp-mainloop.la - -polypaudio_SOURCES = idxset.c idxset.h \ - queue.c queue.h \ - strbuf.c strbuf.h \ - main.c \ - mainloop.c mainloop.h \ - memblock.c memblock.h \ - sample.c sample.h \ - sample-util.c sample-util.h \ - memblockq.c memblockq.h \ - client.c client.h \ - core.c core.h \ - source-output.c source-output.h \ - sink-input.c sink-input.h \ - source.c source.h \ - sink.c sink.h \ - module.c module.h \ - mainloop-signal.c mainloop-signal.h \ - mainloop-api.c mainloop-api.h \ - util.c util.h \ - hashmap.c hashmap.h \ - namereg.c namereg.h \ - sconv.c sconv.h \ - resampler.c resampler.h \ - endianmacros.h \ - memchunk.c memchunk.h \ - sconv-s16le.c sconv-s16le.h \ - sconv-s16be.c sconv-s16be.h \ - sioman.c sioman.h \ - modargs.c modargs.h \ - cmdline.c cmdline.h \ - cli-command.c cli-command.h \ - clitext.c clitext.h \ - tokenizer.c tokenizer.h \ - dynarray.c dynarray.h - -polypaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) -polypaudio_INCLUDES = $(INCLTDL) -polypaudio_LDADD = $(LIBLTDL) $(LIBSAMPLERATE_LIBS) -polypaudio_LDFLAGS=-export-dynamic - -libprotocol_simple_la_SOURCES = protocol-simple.c protocol-simple.h -libprotocol_simple_la_LDFLAGS = -avoid-version -libprotocol_simple_la_LIBADD = libsocket-server.la libiochannel.la - -libsocket_server_la_SOURCES = socket-server.c socket-server.h -libsocket_server_la_LDFLAGS = -avoid-version -libsocket_server_la_LIBADD = libiochannel.la libsocket-util.la - -libsocket_client_la_SOURCES = socket-client.c socket-client.h -libsocket_client_la_LDFLAGS = -avoid-version -libsocket_client_la_LIBADD = libiochannel.la libsocket-util.la - -libpstream_la_SOURCES = pstream.c pstream.h -libpstream_la_LDFLAGS = -avoid-version -libpstream_la_LIBADD = libpacket.la libiochannel.la - -libpstream_util_la_SOURCES = pstream-util.c pstream-util.h -libpstream_util_la_LDFLAGS = -avoid-version -libpstream_util_la_LIBADD = libpacket.la libpstream.la libtagstruct.la - -libpdispatch_la_SOURCES = pdispatch.c pdispatch.h -libpdispatch_la_LDFLAGS = -avoid-version -libpdispatch_la_LIBADD = libtagstruct.la - -libiochannel_la_SOURCES = iochannel.c iochannel.h -libiochannel_la_LDFLAGS = -avoid-version -libiochannel_la_LIBADD = libsocket-util.la - -libpacket_la_SOURCES = packet.c packet.h -libpacket_la_LDFLAGS = -avoid-version - -liboss_util_la_SOURCES = oss-util.c oss-util.h -liboss_util_la_LDFLAGS = -avoid-version - -libalsa_util_la_SOURCES = alsa-util.c alsa-util.h -libalsa_util_la_LDFLAGS = -avoid-version -libalsa_util_la_LIBADD = $(ASOUNDLIB_LIBS) -libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) - -libioline_la_SOURCES = ioline.c ioline.h -libioline_la_LDFLAGS = -avoid-version -libioline_la_LIBADD = libiochannel.la - -libcli_la_SOURCES = cli.c cli.h -libcli_la_LDFLAGS = -avoid-version -libcli_la_LIBADD = libiochannel.la libioline.la - -libprotocol_cli_la_SOURCES = protocol-cli.c protocol-cli.h -libprotocol_cli_la_LDFLAGS = -avoid-version -libprotocol_cli_la_LIBADD = libsocket-server.la libiochannel.la libcli.la - -libprotocol_native_la_SOURCES = protocol-native.c protocol-native.h native-common.h -libprotocol_native_la_LDFLAGS = -avoid-version -libprotocol_native_la_LIBADD = libsocket-server.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la - -libtagstruct_la_SOURCES = tagstruct.c tagstruct.h -libtagstruct_la_LDFLAGS = -avoid-version - -libprotocol_esound_la_SOURCES = protocol-esound.c protocol-esound.h esound.h -libprotocol_esound_la_LDFLAGS = -avoid-version -libprotocol_esound_la_LIBADD = libsocket-server.la libiochannel.la libauthkey.la - -libauthkey_la_SOURCES = authkey.c authkey.h -libauthkey_la_LDFLAGS = -avoid-version - -libsocket_util_la_SOURCES = socket-util.c socket-util.h -libsocket_util_la_LDFLAGS = -avoid-version - -module_simple_protocol_tcp_la_SOURCES = module-protocol-stub.c -module_simple_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS) -module_simple_protocol_tcp_la_LDFLAGS = -module -avoid-version -module_simple_protocol_tcp_la_LIBADD = libprotocol-simple.la libsocket-server.la - -module_simple_protocol_unix_la_SOURCES = module-protocol-stub.c -module_simple_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS) -module_simple_protocol_unix_la_LDFLAGS = -module -avoid-version -module_simple_protocol_unix_la_LIBADD = libprotocol-simple.la libsocket-server.la libsocket-util.la - -module_cli_protocol_tcp_la_SOURCES = module-protocol-stub.c -module_cli_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS) -module_cli_protocol_tcp_la_LDFLAGS = -module -avoid-version -module_cli_protocol_tcp_la_LIBADD = libprotocol-cli.la libsocket-server.la - -module_cli_protocol_unix_la_SOURCES = module-protocol-stub.c -module_cli_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS) -module_cli_protocol_unix_la_LDFLAGS = -module -avoid-version -module_cli_protocol_unix_la_LIBADD = libprotocol-cli.la libsocket-server.la libsocket-util.la - -module_native_protocol_tcp_la_SOURCES = module-protocol-stub.c -module_native_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS) -module_native_protocol_tcp_la_LDFLAGS = -module -avoid-version -module_native_protocol_tcp_la_LIBADD = libprotocol-native.la libsocket-server.la - -module_native_protocol_unix_la_SOURCES = module-protocol-stub.c -module_native_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS) -module_native_protocol_unix_la_LDFLAGS = -module -avoid-version -module_native_protocol_unix_la_LIBADD = libprotocol-native.la libsocket-server.la libsocket-util.la - -module_esound_protocol_tcp_la_SOURCES = module-protocol-stub.c -module_esound_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS) -module_esound_protocol_tcp_la_LDFLAGS = -module -avoid-version -module_esound_protocol_tcp_la_LIBADD = libprotocol-esound.la libsocket-server.la - -module_esound_protocol_unix_la_SOURCES = module-protocol-stub.c -module_esound_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS) -module_esound_protocol_unix_la_LDFLAGS = -module -avoid-version -module_esound_protocol_unix_la_LIBADD = libprotocol-esound.la libsocket-server.la libsocket-util.la - -module_pipe_sink_la_SOURCES = module-pipe-sink.c -module_pipe_sink_la_LDFLAGS = -module -avoid-version -module_pipe_sink_la_LIBADD = libiochannel.la - -module_alsa_sink_la_SOURCES = module-alsa-sink.c -module_alsa_sink_la_LDFLAGS = -module -avoid-version -module_alsa_sink_la_LIBADD = $(ASOUNDLIB_LIBS) libalsa-util.la -module_alsa_sink_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) - -module_alsa_source_la_SOURCES = module-alsa-source.c -module_alsa_source_la_LDFLAGS = -module -avoid-version -module_alsa_source_la_LIBADD = $(ASOUNDLIB_LIBS) libalsa-util.la -module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) - -module_oss_la_SOURCES = module-oss.c -module_oss_la_LDFLAGS = -module -avoid-version -module_oss_la_LIBADD = libiochannel.la liboss-util.la - -module_oss_mmap_la_SOURCES = module-oss-mmap.c -module_oss_mmap_la_LDFLAGS = -module -avoid-version -module_oss_mmap_la_LIBADD = liboss-util.la - -module_cli_la_SOURCES = module-cli.c -module_cli_la_LDFLAGS = -module -avoid-version -module_cli_la_LIBADD = libcli.la libiochannel.la - -libpolyp_la_SOURCES = polyplib.c polyplib.h \ - polyplib-def.h \ - tagstruct.c tagstruct.h \ - iochannel.c iochannel.h \ - pstream.c pstream.h \ - pstream-util.c pstream-util.h \ - pdispatch.c pdispatch.h \ - mainloop-api.c mainloop-api.h \ - idxset.c idxset.h \ - util.c util.h \ - memblock.c memblock.h \ - socket-client.c socket-client.h \ - packet.c packet.h \ - queue.c queue.h \ - dynarray.c dynarray.h \ - memchunk.c memchunk.h \ - authkey.c authkey.h \ - socket-util.c socket-util.h \ - native-common.h -libpolyp_la_CFLAGS = $(AM_CFLAGS) - -libpolyp_mainloop_la_SOURCES = mainloop-api.h mainloop-api.c \ - mainloop.c mainloop.h \ - mainloop-signal.c mainloop-signal.h -libpolyp_mainloop_la_CFLAGS = $(AM_CFLAGS) - -libpolyp_error_la_SOURCES = polyplib-error.c polyplib-error.h -libpolyp_error_la_CFLAGS = $(AM_CFLAGS) - -libpolyp_simple_la_SOURCES = polyplib-simple.c polyplib-simple.h -libpolyp_simple_la_CFLAGS = $(AM_CFLAGS) -libpolyp_simple_la_LIBADD = libpolyp.la libpolyp-mainloop.la - -pacat_SOURCES = pacat.c -pacat_LDADD = libpolyp.la libpolyp-error.la libpolyp-mainloop.la -pacat_CFLAGS = $(AM_CFLAGS) - -pactl_SOURCES = pactl.c -pactl_LDADD = libpolyp.la libpolyp-error.la libpolyp-mainloop.la -pactl_CFLAGS = $(AM_CFLAGS) - -pacat_simple_SOURCES = pacat-simple.c -pacat_simple_LDADD = libpolyp-simple.la libpolyp-error.la -pacat_simple_CFLAGS = $(AM_CFLAGS) - -parec_simple_SOURCES = parec-simple.c -parec_simple_LDADD = libpolyp-simple.la libpolyp-error.la -parec_simple_CFLAGS = $(AM_CFLAGS) - -if BUILD_LIBPOLYPCORE - -pkginclude_HEADERS+=cli-command.h\ - client.h \ - core.h \ - dynarray.h \ - endianmacros.h \ - hashmap.h \ - idxset.h \ - iochannel.h \ - memblock.h \ - memblockq.h \ - memchunk.h \ - modargs.h \ - module.h \ - namereg.h \ - queue.h \ - resampler.h \ - sample-util.h \ - sink.h \ - sink-input.h \ - sioman.h \ - socket-server.h \ - socket-client.h \ - socket-util.h \ - source.h \ - source-output.h \ - strbuf.h \ - tokenizer.h \ - tagstruct.h \ - util.h - -lib_LTLIBRARIES+= libpolypcore.la - -libpolypcore_la_SOURCES = idxset.c idxset.h \ - queue.c queue.h \ - strbuf.c strbuf.h \ - mainloop.c mainloop.h \ - memblock.c memblock.h \ - sample.c sample.h \ - sample-util.c sample-util.h \ - memblockq.c memblockq.h \ - client.c client.h \ - core.c core.h \ - source-output.c source-output.h \ - sink-input.c sink-input.h \ - source.c source.h \ - sink.c sink.h \ - module.c module.h \ - mainloop-signal.c mainloop-signal.h \ - mainloop-api.c mainloop-api.h \ - util.c util.h \ - hashmap.c hashmap.h \ - namereg.c namereg.h \ - sconv.c sconv.h \ - resampler.c resampler.h \ - endianmacros.h \ - memchunk.c memchunk.h \ - sconv-s16le.c sconv-s16le.h \ - sconv-s16be.c sconv-s16be.h \ - sioman.c sioman.h \ - modargs.c modargs.h \ - cli-command.c cli-command.h \ - clitext.c clitext.h \ - tokenizer.c tokenizer.h \ - dynarray.c dynarray.h - -endif diff --git a/src/alsa-util.c b/src/alsa-util.c deleted file mode 100644 index 7f266df5..00000000 --- a/src/alsa-util.c +++ /dev/null @@ -1,98 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include "alsa-util.h" -#include "sample.h" - -int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, struct pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *buffer_size) { - int ret = 0; - snd_pcm_hw_params_t *hwparams = NULL; - static const snd_pcm_format_t format_trans[] = { - [PA_SAMPLE_U8] = SND_PCM_FORMAT_U8, - [PA_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW, - [PA_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW, - [PA_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE, - [PA_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE, - [PA_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE, - [PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE, - }; - - if (snd_pcm_hw_params_malloc(&hwparams) < 0 || - snd_pcm_hw_params_any(pcm_handle, hwparams) < 0 || - snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 || - snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[ss->format]) < 0 || - snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &ss->rate, NULL) < 0 || - snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss->channels) < 0 || - snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, periods, NULL) < 0 || - snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, buffer_size) < 0 || - snd_pcm_hw_params(pcm_handle, hwparams) < 0) { - ret = -1; - } - - if (hwparams) - snd_pcm_hw_params_free(hwparams); - return ret; -} - -int pa_create_io_sources(snd_pcm_t *pcm_handle, struct pa_mainloop_api* m, void ***io_sources, unsigned *n_io_sources, void (*cb)(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata) { - unsigned i; - struct pollfd *pfds, *ppfd; - void **ios; - assert(pcm_handle && m && io_sources && n_io_sources && cb); - - *n_io_sources = snd_pcm_poll_descriptors_count(pcm_handle); - - pfds = malloc(sizeof(struct pollfd) * *n_io_sources); - assert(pfds); - if (snd_pcm_poll_descriptors(pcm_handle, pfds, *n_io_sources) < 0) { - free(pfds); - return -1; - } - - *io_sources = malloc(sizeof(void*) * *n_io_sources); - assert(io_sources); - - for (i = 0, ios = *io_sources, ppfd = pfds; i < *n_io_sources; i++, ios++, ppfd++) { - *ios = m->source_io(m, ppfd->fd, - ((ppfd->events & POLLIN) ? PA_MAINLOOP_API_IO_EVENT_INPUT : 0) | - ((ppfd->events & POLLOUT) ? PA_MAINLOOP_API_IO_EVENT_OUTPUT : 0), cb, userdata); - assert(*ios); - } - - free(pfds); - return 0; -} - -void pa_free_io_sources(struct pa_mainloop_api* m, void **io_sources, unsigned n_io_sources) { - unsigned i; - void **ios; - assert(m && io_sources); - - for (ios = io_sources, i = 0; i < n_io_sources; i++, ios++) - m->cancel_io(m, *ios); - free(io_sources); -} diff --git a/src/alsa-util.h b/src/alsa-util.h deleted file mode 100644 index 03e427d9..00000000 --- a/src/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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -#include "sample.h" -#include "mainloop-api.h" - -int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, struct pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *buffer_size); - -int pa_create_io_sources(snd_pcm_t *pcm_handle, struct pa_mainloop_api *m, void ***io_sources, unsigned *n_io_sources, void (*cb)(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata); -void pa_free_io_sources(struct pa_mainloop_api* m, void **io_sources, unsigned n_io_sources); - -#endif diff --git a/src/authkey.c b/src/authkey.c deleted file mode 100644 index 3180b8fd..00000000 --- a/src/authkey.c +++ /dev/null @@ -1,151 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "authkey.h" -#include "util.h" - -#define RANDOM_DEVICE "/dev/urandom" - -static int load(const char *fn, void *data, size_t length) { - int fd = -1, ret = -1; - ssize_t r; - - assert(fn && data && length); - - if ((fd = open(fn, O_RDONLY)) < 0) - goto finish; - - if ((r = pa_loop_read(fd, data, length)) < 0 || (size_t) r != length) { - ret = -2; - goto finish; - } - - ret = 0; - -finish: - if (fd >= 0) - close(fd); - - return ret; -} - -static int generate(const char *fn, void *data, size_t length) { - int fd = -1, random_fd = -1, ret = -1; - ssize_t r; - assert(fn && data && length); - - if ((fd = open(fn, O_WRONLY|O_EXCL|O_CREAT, S_IRUSR | S_IWUSR)) < 0) - goto finish; - - if ((random_fd = open(RANDOM_DEVICE, O_RDONLY)) >= 0) { - - if ((r = pa_loop_read(random_fd, data, length)) < 0 || (size_t) r != length) { - ret = -2; - goto finish; - } - - } else { - uint8_t *p; - size_t l; - fprintf(stderr, "WARNING: Failed to open entropy device '"RANDOM_DEVICE"': %s, falling back to unsecure pseudo RNG.\n", strerror(errno)); - - srandom(time(NULL)); - - for (p = data, l = length; l > 0; p++, l--) - *p = (uint8_t) random(); - } - - if ((r = pa_loop_write(fd, data, length)) < 0 || (size_t) r != length) { - ret = -2; - goto finish; - } - - ret = 0; - -finish: - if (fd >= 0) { - if (ret != 0) - unlink(fn); - close(fd); - } - if (random_fd >= 0) - close(random_fd); - - return ret; -} - -int pa_authkey_load(const char *path, void *data, size_t length) { - int ret, i; - - assert(path && data && length); - - for (i = 0; i < 10; i++) { - if ((ret = load(path, data, length)) < 0) - if (ret == -1 && errno == ENOENT) - if ((ret = generate(path, data, length)) < 0) - if (ret == -1 && errno == EEXIST) - continue; - break; - } - - if (ret < 0) - fprintf(stderr, "Failed to load authorization key '%s': %s\n", path, (ret == -1) ? strerror(errno) : "file corrupt"); - - return ret; -} - -int pa_authkey_load_from_home(const char *fn, void *data, size_t length) { - char *home; - char path[PATH_MAX]; - - assert(fn && data && length); - - if (!(home = getenv("HOME"))) - return -2; - - snprintf(path, sizeof(path), "%s/%s", home, fn); - - return pa_authkey_load(path, data, length); -} - -int pa_authkey_load_auto(const char *fn, void *data, size_t length) { - assert(fn && data && length); - - if (*fn == '/') - return pa_authkey_load(fn, data, length); - else - return pa_authkey_load_from_home(fn, data, length); -} diff --git a/src/authkey.h b/src/authkey.h deleted file mode 100644 index acdcc24d..00000000 --- a/src/authkey.h +++ /dev/null @@ -1,31 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -int pa_authkey_load(const char *path, void *data, size_t len); -int pa_authkey_load_from_home(const char *fn, void *data, size_t length); -int pa_authkey_load_auto(const char *fn, void *data, size_t length); - -#endif diff --git a/src/cli-command.c b/src/cli-command.c deleted file mode 100644 index f3a2f8a0..00000000 --- a/src/cli-command.c +++ /dev/null @@ -1,557 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#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 "clitext.h" - -struct command { - const char *name; - int (*proc) (struct pa_core *c, struct pa_tokenizer*t, struct pa_strbuf *buf, int *fail, int *verbose); - const char *help; - unsigned args; -}; - -static int pa_cli_command_exit(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_help(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_modules(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_clients(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_sinks(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_sources(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_sink_inputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_source_outputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_stat(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_info(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_unload(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_sink_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_sink_input_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_sink_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_source_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_kill_client(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_kill_sink_input(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); -static int pa_cli_command_kill_source_output(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose); - -static const struct command commands[] = { - { "exit", pa_cli_command_exit, "Terminate the daemon", 1 }, - { "help", pa_cli_command_help, "Show this help", 1 }, - { "modules", pa_cli_command_modules, "List loaded modules", 1 }, - { "sinks", pa_cli_command_sinks, "List loaded sinks", 1 }, - { "sources", pa_cli_command_sources, "List loaded sources", 1 }, - { "clients", pa_cli_command_clients, "List loaded clients", 1 }, - { "sink_inputs", pa_cli_command_sink_inputs, "List sink inputs", 1 }, - { "source_outputs", pa_cli_command_source_outputs, "List source outputs", 1 }, - { "stat", pa_cli_command_stat, "Show memory block statistics", 1 }, - { "info", pa_cli_command_info, "Show comprehensive status", 1 }, - { "ls", pa_cli_command_info, NULL, 1 }, - { "list", pa_cli_command_info, NULL, 1 }, - { "load", pa_cli_command_load, "Load a module (args: name, arguments)", 3}, - { "unload", pa_cli_command_unload, "Unload a module (args: index)", 2}, - { "sink_volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3}, - { "sink_input_volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index|name, volume)", 3}, - { "sink_default", pa_cli_command_sink_default, "Set the default sink (args: index|name)", 2}, - { "source_default", pa_cli_command_source_default, "Set the default source (args: index|name)", 2}, - { "kill_client", pa_cli_command_kill_client, "Kill a client (args: index)", 2}, - { "kill_sink_input", pa_cli_command_kill_sink_input, "Kill a sink input (args: index)", 2}, - { "kill_source_output", pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2}, - { NULL, NULL, NULL, 0 } -}; - -static const char whitespace[] = " \t\n\r"; -static const char linebreak[] = "\n\r"; - -static uint32_t parse_index(const char *n) { - long index; - char *x; - index = strtol(n, &x, 0); - if (!x || *x != 0 || index < 0) - return (uint32_t) PA_IDXSET_INVALID; - - return (uint32_t) index; -} - -static int pa_cli_command_exit(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - assert(c && c->mainloop && t); - c->mainloop->quit(c->mainloop, 0); - return 0; -} - -static int pa_cli_command_help(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - const struct command*command; - assert(c && t && buf); - - pa_strbuf_puts(buf, "Available commands:\n"); - - for (command = commands; command->name; command++) - if (command->help) - pa_strbuf_printf(buf, " %-20s %s\n", command->name, command->help); - return 0; -} - -static int pa_cli_command_modules(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - char *s; - assert(c && t); - s = pa_module_list_to_string(c); - assert(s); - pa_strbuf_puts(buf, s); - free(s); - return 0; -} - -static int pa_cli_command_clients(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - char *s; - assert(c && t); - s = pa_client_list_to_string(c); - assert(s); - pa_strbuf_puts(buf, s); - free(s); - return 0; -} - -static int pa_cli_command_sinks(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - char *s; - assert(c && t); - s = pa_sink_list_to_string(c); - assert(s); - pa_strbuf_puts(buf, s); - free(s); - return 0; -} - -static int pa_cli_command_sources(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - char *s; - assert(c && t); - s = pa_source_list_to_string(c); - assert(s); - pa_strbuf_puts(buf, s); - free(s); - return 0; -} - -static int pa_cli_command_sink_inputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - char *s; - assert(c && t); - s = pa_sink_input_list_to_string(c); - assert(s); - pa_strbuf_puts(buf, s); - free(s); - return 0; -} - -static int pa_cli_command_source_outputs(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - char *s; - assert(c && t); - s = pa_source_output_list_to_string(c); - assert(s); - pa_strbuf_puts(buf, s); - free(s); - return 0; -} - -static int pa_cli_command_stat(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - assert(c && t); - pa_strbuf_printf(buf, "Memory blocks allocated: %u, total size: %u bytes.\n", pa_memblock_get_count(), pa_memblock_get_total()); - return 0; -} - -static int pa_cli_command_info(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - assert(c && t); - pa_cli_command_stat(c, t, buf, fail, verbose); - pa_cli_command_modules(c, t, buf, fail, verbose); - pa_cli_command_sinks(c, t, buf, fail, verbose); - pa_cli_command_sources(c, t, buf, fail, verbose); - pa_cli_command_clients(c, t, buf, fail, verbose); - pa_cli_command_sink_inputs(c, t, buf, fail, verbose); - pa_cli_command_source_outputs(c, t, buf, fail, verbose); - return 0; -} - -static int pa_cli_command_load(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - struct pa_module *m; - const char *name; - char txt[256]; - assert(c && t); - - if (!(name = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify the module name and optionally arguments.\n"); - return -1; - } - - if (!(m = pa_module_load(c, name, pa_tokenizer_get(t, 2)))) { - pa_strbuf_puts(buf, "Module load failed.\n"); - return -1; - } - - if (*verbose) { - snprintf(txt, sizeof(txt), "Module successfully loaded, index: %u.\n", m->index); - pa_strbuf_puts(buf, txt); - } - return 0; -} - -static int pa_cli_command_unload(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - struct pa_module *m; - uint32_t index; - const char *i; - char *e; - assert(c && t); - - if (!(i = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify the module index.\n"); - return -1; - } - - index = (uint32_t) strtoul(i, &e, 10); - if (*e || !(m = pa_idxset_get_by_index(c->modules, index))) { - pa_strbuf_puts(buf, "Invalid module index.\n"); - return -1; - } - - pa_module_unload_request(c, m); - return 0; -} - -static int pa_cli_command_sink_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - const char *n, *v; - char *x = NULL; - struct pa_sink *sink; - long volume; - - if (!(n = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n"); - return -1; - } - - if (!(v = pa_tokenizer_get(t, 2))) { - pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); - return -1; - } - - volume = strtol(v, &x, 0); - if (!x || *x != 0 || volume < 0) { - pa_strbuf_puts(buf, "Failed to parse volume.\n"); - return -1; - } - - if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) { - pa_strbuf_puts(buf, "No sink found by this name or index.\n"); - return -1; - } - - sink->volume = (uint32_t) volume; - return 0; -} - -static int pa_cli_command_sink_input_volume(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - const char *n, *v; - struct pa_sink_input *si; - long volume; - uint32_t index; - char *x; - - if (!(n = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n"); - return -1; - } - - if ((index = parse_index(n)) == PA_IDXSET_INVALID) { - pa_strbuf_puts(buf, "Failed to parse index.\n"); - return -1; - } - - if (!(v = pa_tokenizer_get(t, 2))) { - pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); - return -1; - } - - x = NULL; - volume = strtol(v, &x, 0); - if (!x || *x != 0 || volume < 0) { - pa_strbuf_puts(buf, "Failed to parse volume.\n"); - return -1; - } - - if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) index))) { - pa_strbuf_puts(buf, "No sink input found with this index.\n"); - return -1; - } - - si->volume = (uint32_t) volume; - return 0; -} - -static int pa_cli_command_sink_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - const char *n; - struct pa_sink *sink; - assert(c && t); - - if (!(n = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n"); - return -1; - } - - if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) { - pa_strbuf_puts(buf, "No sink found by this name or index.\n"); - return -1; - } - - c->default_sink_index = sink->index; - return 0; -} - -static int pa_cli_command_source_default(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - const char *n; - struct pa_source *source; - assert(c && t); - - if (!(n = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n"); - return -1; - } - - if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) { - pa_strbuf_puts(buf, "No source found by this name or index.\n"); - return -1; - } - - c->default_source_index = source->index; - return 0; -} - -static int pa_cli_command_kill_client(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - const char *n; - struct pa_client *client; - uint32_t index; - assert(c && t); - - if (!(n = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a client by its index.\n"); - return -1; - } - - if ((index = parse_index(n)) == PA_IDXSET_INVALID) { - pa_strbuf_puts(buf, "Failed to parse index.\n"); - return -1; - } - - if (!(client = pa_idxset_get_by_index(c->clients, index))) { - pa_strbuf_puts(buf, "No client found by this index.\n"); - return -1; - } - - pa_client_kill(client); - return 0; -} - -static int pa_cli_command_kill_sink_input(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - const char *n; - struct pa_sink_input *sink_input; - uint32_t index; - assert(c && t); - - if (!(n = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a sink input by its index.\n"); - return -1; - } - - if ((index = parse_index(n)) == PA_IDXSET_INVALID) { - pa_strbuf_puts(buf, "Failed to parse index.\n"); - return -1; - } - - if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, index))) { - pa_strbuf_puts(buf, "No sink input found by this index.\n"); - return -1; - } - - pa_sink_input_kill(sink_input); - return 0; -} - -static int pa_cli_command_kill_source_output(struct pa_core *c, struct pa_tokenizer *t, struct pa_strbuf *buf, int *fail, int *verbose) { - const char *n; - struct pa_source_output *source_output; - uint32_t index; - assert(c && t); - - if (!(n = pa_tokenizer_get(t, 1))) { - pa_strbuf_puts(buf, "You need to specify a source output by its index.\n"); - return -1; - } - - if ((index = parse_index(n)) == PA_IDXSET_INVALID) { - pa_strbuf_puts(buf, "Failed to parse index.\n"); - return -1; - } - - if (!(source_output = pa_idxset_get_by_index(c->source_outputs, index))) { - pa_strbuf_puts(buf, "No source output found by this index.\n"); - return -1; - } - - pa_source_output_kill(source_output); - return 0; -} - -int pa_cli_command_execute_line(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose) { - const char *cs; - - cs = s+strspn(s, whitespace); - - if (*cs == '#' || !*cs) - return 0; - else if (*cs == '.') { - static const char fail_meta[] = ".fail"; - static const char nofail_meta[] = ".nofail"; - static const char verbose_meta[] = ".verbose"; - static const char noverbose_meta[] = ".noverbose"; - - if (!strcmp(cs, verbose_meta)) - *verbose = 1; - else if (!strcmp(cs, noverbose_meta)) - *verbose = 0; - else if (!strcmp(cs, fail_meta)) - *fail = 1; - else if (!strcmp(cs, nofail_meta)) - *fail = 0; - else { - size_t l; - static const char include_meta[] = ".include"; - l = strcspn(cs, whitespace); - - if (l == sizeof(include_meta)-1 && !strncmp(cs, include_meta, l)) { - const char *filename = cs+l+strspn(cs+l, whitespace); - - if (pa_cli_command_execute_file(c, filename, buf, fail, verbose) < 0) - if (*fail) return -1; - } else { - pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs); - if (*fail) return -1; - } - } - } else { - const struct command*command; - int unknown = 1; - size_t l; - - l = strcspn(cs, whitespace); - - for (command = commands; command->name; command++) - if (strlen(command->name) == l && !strncmp(cs, command->name, l)) { - int ret; - struct pa_tokenizer *t = pa_tokenizer_new(cs, command->args); - assert(t); - ret = command->proc(c, t, buf, fail, verbose); - pa_tokenizer_free(t); - unknown = 0; - - if (ret < 0 && *fail) - return -1; - - break; - } - - if (unknown) { - pa_strbuf_printf(buf, "Unknown command: %s\n", cs); - if (*fail) - return -1; - } - } - - return 0; -} - -int pa_cli_command_execute_file(struct pa_core *c, const char *fn, struct pa_strbuf *buf, int *fail, int *verbose) { - char line[256]; - FILE *f = NULL; - int ret = -1; - assert(c && fn && buf); - - if (!(f = fopen(fn, "r"))) { - pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, strerror(errno)); - if (!*fail) - ret = 0; - goto fail; - } - - if (*verbose) - pa_strbuf_printf(buf, "Executing file: '%s'\n", fn); - - while (fgets(line, sizeof(line), f)) { - char *e = line + strcspn(line, linebreak); - *e = 0; - - if (pa_cli_command_execute_line(c, line, buf, fail, verbose) < 0 && *fail) - goto fail; - } - - if (*verbose) - pa_strbuf_printf(buf, "Executed file: '%s'\n", fn); - - ret = 0; - -fail: - if (f) - fclose(f); - - return ret; -} - -int pa_cli_command_execute(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose) { - const char *p; - assert(c && s && buf && fail && verbose); - - p = s; - while (*p) { - size_t l = strcspn(p, linebreak); - char *line = strndup(p, l); - assert(line); - - if (pa_cli_command_execute_line(c, line, buf, fail, verbose) < 0&& *fail) { - free(line); - return -1; - } - free(line); - - p += l; - p += strspn(p, linebreak); - } - - return 0; -} diff --git a/src/cli-command.h b/src/cli-command.h deleted file mode 100644 index b3c601ad..00000000 --- a/src/cli-command.h +++ /dev/null @@ -1,32 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "strbuf.h" -#include "core.h" - -int pa_cli_command_execute_line(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose); -int pa_cli_command_execute_file(struct pa_core *c, const char *fn, struct pa_strbuf *buf, int *fail, int *verbose); -int pa_cli_command_execute(struct pa_core *c, const char *s, struct pa_strbuf *buf, int *fail, int *verbose); - -#endif diff --git a/src/cli.c b/src/cli.c deleted file mode 100644 index f2aa5a65..00000000 --- a/src/cli.c +++ /dev/null @@ -1,148 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#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 "clitext.h" -#include "cli-command.h" - -struct pa_cli { - struct pa_core *core; - struct pa_ioline *line; - - void (*eof_callback)(struct pa_cli *c, void *userdata); - void *userdata; - - struct pa_client *client; - - int fail, verbose, kill_requested, defer_kill; -}; - -static void line_callback(struct pa_ioline *line, const char *s, void *userdata); - -static const char prompt[] = ">>> "; - -static void client_kill(struct pa_client *c); - -struct pa_cli* pa_cli_new(struct pa_core *core, struct pa_iochannel *io, struct pa_module *m) { - char cname[256]; - struct pa_cli *c; - assert(io); - - c = malloc(sizeof(struct pa_cli)); - assert(c); - c->core = core; - c->line = pa_ioline_new(io); - assert(c->line); - - c->userdata = NULL; - c->eof_callback = NULL; - - pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); - c->client = pa_client_new(core, "CLI", cname); - assert(c->client); - c->client->kill = client_kill; - c->client->userdata = c; - c->client->owner = m; - - pa_ioline_set_callback(c->line, line_callback, c); - pa_ioline_puts(c->line, "Welcome to polypaudio! Use \"help\" for usage information.\n"); - pa_ioline_puts(c->line, prompt); - - c->fail = c->kill_requested = c->defer_kill = 0; - c->verbose = 1; - - return c; -} - -void pa_cli_free(struct pa_cli *c) { - assert(c); - pa_ioline_free(c->line); - pa_client_free(c->client); - free(c); -} - -static void client_kill(struct pa_client *client) { - struct pa_cli *c; - assert(client && client->userdata); - c = client->userdata; - - fprintf(stderr, "CLI client killed.\n"); - if (c->defer_kill) - c->kill_requested = 1; - else { - if (c->eof_callback) - c->eof_callback(c, c->userdata); - } -} - -static void line_callback(struct pa_ioline *line, const char *s, void *userdata) { - struct pa_strbuf *buf; - struct pa_cli *c = userdata; - char *p; - assert(line && c); - - if (!s) { - fprintf(stderr, "CLI got EOF from user.\n"); - if (c->eof_callback) - c->eof_callback(c, c->userdata); - - return; - } - - buf = pa_strbuf_new(); - assert(buf); - c->defer_kill++; - pa_cli_command_execute_line(c->core, s, buf, &c->fail, &c->verbose); - c->defer_kill--; - pa_ioline_puts(line, p = pa_strbuf_tostring_free(buf)); - free(p); - - if (c->kill_requested) { - if (c->eof_callback) - c->eof_callback(c, c->userdata); - } else - pa_ioline_puts(line, prompt); -} - -void pa_cli_set_eof_callback(struct pa_cli *c, void (*cb)(struct pa_cli*c, void *userdata), void *userdata) { - assert(c && cb); - c->eof_callback = cb; - c->userdata = userdata; -} diff --git a/src/cli.h b/src/cli.h deleted file mode 100644 index 9cfee0d8..00000000 --- a/src/cli.h +++ /dev/null @@ -1,36 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "iochannel.h" -#include "core.h" -#include "module.h" - -struct pa_cli; - -struct pa_cli* pa_cli_new(struct pa_core *core, struct pa_iochannel *io, struct pa_module *m); -void pa_cli_free(struct pa_cli *cli); - -void pa_cli_set_eof_callback(struct pa_cli *cli, void (*cb)(struct pa_cli*c, void *userdata), void *userdata); - -#endif diff --git a/src/client.c b/src/client.c deleted file mode 100644 index 0294c9e2..00000000 --- a/src/client.c +++ /dev/null @@ -1,79 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include "client.h" - -struct pa_client *pa_client_new(struct pa_core *core, const char *protocol_name, char *name) { - struct pa_client *c; - int r; - assert(core); - - c = malloc(sizeof(struct pa_client)); - assert(c); - c->name = name ? strdup(name) : NULL; - c->owner = NULL; - c->core = core; - c->protocol_name = protocol_name; - - c->kill = NULL; - c->userdata = NULL; - - r = pa_idxset_put(core->clients, c, &c->index); - assert(c->index != PA_IDXSET_INVALID && r >= 0); - - fprintf(stderr, "client: created %u \"%s\"\n", c->index, c->name); - - return c; -} - -void pa_client_free(struct pa_client *c) { - assert(c && c->core); - - pa_idxset_remove_by_data(c->core->clients, c, NULL); - fprintf(stderr, "client: freed %u \"%s\"\n", c->index, c->name); - free(c->name); - free(c); -} - -void pa_client_kill(struct pa_client *c) { - assert(c); - if (!c->kill) { - fprintf(stderr, "kill() operation not implemented for client %u\n", c->index); - return; - } - - c->kill(c); -} - -void pa_client_rename(struct pa_client *c, const char *name) { - assert(c); - free(c->name); - c->name = name ? strdup(name) : NULL; -} diff --git a/src/client.h b/src/client.h deleted file mode 100644 index c926208e..00000000 --- a/src/client.h +++ /dev/null @@ -1,51 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "core.h" -#include "module.h" - -struct pa_client { - uint32_t index; - - struct pa_module *owner; - char *name; - struct pa_core *core; - const char *protocol_name; - - void (*kill)(struct pa_client *c); - void *userdata; -}; - -struct pa_client *pa_client_new(struct pa_core *c, const char *protocol_name, char *name); - -/* This function should be called only by the code that created the client */ -void pa_client_free(struct pa_client *c); - -/* Code that didn't create the client should call this function to - * request destruction of the client */ -void pa_client_kill(struct pa_client *c); - -void pa_client_rename(struct pa_client *c, const char *name); - -#endif diff --git a/src/clitext.c b/src/clitext.c deleted file mode 100644 index c1b9953b..00000000 --- a/src/clitext.c +++ /dev/null @@ -1,203 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include "clitext.h" -#include "module.h" -#include "client.h" -#include "sink.h" -#include "source.h" -#include "sink-input.h" -#include "source-output.h" -#include "strbuf.h" -#include "sample-util.h" - -char *pa_module_list_to_string(struct pa_core *c) { - struct pa_strbuf *s; - struct pa_module *m; - uint32_t index = PA_IDXSET_INVALID; - assert(c); - - s = pa_strbuf_new(); - assert(s); - - pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_ncontents(c->modules)); - - for (m = pa_idxset_first(c->modules, &index); m; m = pa_idxset_next(c->modules, &index)) - pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\targument: <%s>\n", m->index, m->name, m->argument); - - return pa_strbuf_tostring_free(s); -} - -char *pa_client_list_to_string(struct pa_core *c) { - struct pa_strbuf *s; - struct pa_client *client; - uint32_t index = PA_IDXSET_INVALID; - assert(c); - - s = pa_strbuf_new(); - assert(s); - - pa_strbuf_printf(s, "%u client(s).\n", pa_idxset_ncontents(c->clients)); - - for (client = pa_idxset_first(c->clients, &index); client; client = pa_idxset_next(c->clients, &index)) { - pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\tprotocol_name: <%s>\n", client->index, client->name, client->protocol_name); - - if (client->owner) - pa_strbuf_printf(s, "\towner module: <%u>\n", client->owner->index); - } - - return pa_strbuf_tostring_free(s); -} - -char *pa_sink_list_to_string(struct pa_core *c) { - struct pa_strbuf *s; - struct pa_sink *sink, *default_sink; - uint32_t index = PA_IDXSET_INVALID; - assert(c); - - s = pa_strbuf_new(); - assert(s); - - pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_ncontents(c->sinks)); - - default_sink = pa_sink_get_default(c); - - for (sink = pa_idxset_first(c->sinks, &index); sink; sink = pa_idxset_next(c->sinks, &index)) { - char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH]; - pa_sample_snprint(ss, sizeof(ss), &sink->sample_spec); - assert(sink->monitor_source); - pa_strbuf_printf( - s, - " %c index: %u\n\tname: <%s>\n\tvolume: <0x%04x>\n\tlatency: <%u usec>\n\tmonitor_source: <%u>\n\tsample_spec: <%s>\n", - sink == default_sink ? '*' : ' ', - sink->index, sink->name, - (unsigned) sink->volume, - pa_sink_get_latency(sink), - sink->monitor_source->index, - ss); - - if (sink->owner) - pa_strbuf_printf(s, "\towner module: <%u>\n", sink->owner->index); - if (sink->description) - pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description); - } - - return pa_strbuf_tostring_free(s); -} - -char *pa_source_list_to_string(struct pa_core *c) { - struct pa_strbuf *s; - struct pa_source *source, *default_source; - uint32_t index = PA_IDXSET_INVALID; - assert(c); - - s = pa_strbuf_new(); - assert(s); - - pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_ncontents(c->sources)); - - default_source = pa_source_get_default(c); - - for (source = pa_idxset_first(c->sources, &index); source; source = pa_idxset_next(c->sources, &index)) { - char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH]; - pa_sample_snprint(ss, sizeof(ss), &source->sample_spec); - pa_strbuf_printf(s, " %c index: %u\n\tname: <%s>\n\tsample_spec: <%s>\n", source == default_source ? '*' : ' ', source->index, source->name, ss); - - if (source->monitor_of) - pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index); - if (source->owner) - pa_strbuf_printf(s, "\towner module: <%u>\n", source->owner->index); - if (source->description) - pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description); - } - - return pa_strbuf_tostring_free(s); -} - - -char *pa_source_output_list_to_string(struct pa_core *c) { - struct pa_strbuf *s; - struct pa_source_output *o; - uint32_t index = PA_IDXSET_INVALID; - assert(c); - - s = pa_strbuf_new(); - assert(s); - - pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_ncontents(c->source_outputs)); - - for (o = pa_idxset_first(c->source_outputs, &index); o; o = pa_idxset_next(c->source_outputs, &index)) { - char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH]; - pa_sample_snprint(ss, sizeof(ss), &o->sample_spec); - assert(o->source); - pa_strbuf_printf( - s, " index: %u\n\tname: <%s>\n\tsource: <%u>\n\tsample_spec: <%s>\n", - o->index, - o->name, - o->source->index, - ss); - if (o->owner) - pa_strbuf_printf(s, "\towner module: <%u>\n", o->owner->index); - if (o->client) - pa_strbuf_printf(s, "\tclient: <%u>\n", o->client->index); - } - - return pa_strbuf_tostring_free(s); -} - -char *pa_sink_input_list_to_string(struct pa_core *c) { - struct pa_strbuf *s; - struct pa_sink_input *i; - uint32_t index = PA_IDXSET_INVALID; - assert(c); - - s = pa_strbuf_new(); - assert(s); - - pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_ncontents(c->sink_inputs)); - - for (i = pa_idxset_first(c->sink_inputs, &index); i; i = pa_idxset_next(c->sink_inputs, &index)) { - char ss[PA_SAMPLE_SNPRINT_MAX_LENGTH]; - pa_sample_snprint(ss, sizeof(ss), &i->sample_spec); - assert(i->sink); - pa_strbuf_printf( - s, " index: %u\n\tname: <%s>\n\tsink: <%u>\n\tvolume: <0x%04x>\n\tlatency: <%u usec>\n\tsample_spec: <%s>\n", - i->index, - i->name, - i->sink->index, - (unsigned) i->volume, - pa_sink_input_get_latency(i), - ss); - - if (i->owner) - pa_strbuf_printf(s, "\towner module: <%u>\n", i->owner->index); - if (i->client) - pa_strbuf_printf(s, "\tclient: <%u>\n", i->client->index); - } - - return pa_strbuf_tostring_free(s); -} diff --git a/src/clitext.h b/src/clitext.h deleted file mode 100644 index b1718cb5..00000000 --- a/src/clitext.h +++ /dev/null @@ -1,35 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "core.h" - -char *pa_sink_input_list_to_string(struct pa_core *c); -char *pa_source_output_list_to_string(struct pa_core *c); -char *pa_sink_list_to_string(struct pa_core *core); -char *pa_source_list_to_string(struct pa_core *c); -char *pa_client_list_to_string(struct pa_core *c); -char *pa_module_list_to_string(struct pa_core *c); - -#endif - diff --git a/src/cmdline.c b/src/cmdline.c deleted file mode 100644 index a3330145..00000000 --- a/src/cmdline.c +++ /dev/null @@ -1,111 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include - -#include "cmdline.h" -#include "util.h" -#include "strbuf.h" - -void pa_cmdline_help(const char *argv0) { - const char *e; - - if ((e = strrchr(argv0, '/'))) - e++; - else - e = argv0; - - printf("%s [options]\n" - " -L MODULE Load the specified plugin module with the specified argument\n" - " -F FILE Run the specified script\n" - " -C Open a command line on the running TTY\n" - " -D Daemonize after loading the modules\n" - " -f Dont quit when the startup fails\n" - " -v Verbose startup\n" - " -h Show this help\n", e); -} - -struct pa_cmdline* pa_cmdline_parse(int argc, char * const argv []) { - char c; - struct pa_cmdline *cmdline = NULL; - struct pa_strbuf *buf = NULL; - assert(argc && argv); - - cmdline = malloc(sizeof(struct pa_cmdline)); - assert(cmdline); - cmdline->daemonize = cmdline->help = cmdline->verbose = 0; - cmdline->fail = 1; - - buf = pa_strbuf_new(); - assert(buf); - - while ((c = getopt(argc, argv, "L:F:CDhfv")) != -1) { - switch (c) { - case 'L': - pa_strbuf_printf(buf, "load %s\n", optarg); - break; - case 'F': - pa_strbuf_printf(buf, ".include %s\n", optarg); - break; - case 'C': - pa_strbuf_puts(buf, "load module-cli\n"); - break; - case 'D': - cmdline->daemonize = 1; - break; - case 'h': - cmdline->help = 1; - break; - case 'f': - cmdline->fail = 0; - break; - case 'v': - cmdline->verbose = 0; - break; - default: - goto fail; - } - } - - cmdline->cli_commands = pa_strbuf_tostring_free(buf); - return cmdline; - -fail: - if (cmdline) - pa_cmdline_free(cmdline); - if (buf) - pa_strbuf_free(buf); - return NULL; -} - -void pa_cmdline_free(struct pa_cmdline *cmd) { - assert(cmd); - free(cmd->cli_commands); - free(cmd); -} diff --git a/src/cmdline.h b/src/cmdline.h deleted file mode 100644 index 95ce91de..00000000 --- a/src/cmdline.h +++ /dev/null @@ -1,36 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - - -struct pa_cmdline { - int daemonize, help, fail, verbose; - char *cli_commands; -}; - -struct pa_cmdline* pa_cmdline_parse(int argc, char * const argv []); -void pa_cmdline_free(struct pa_cmdline *cmd); - -void pa_cmdline_help(const char *argv0); - -#endif diff --git a/src/core.c b/src/core.c deleted file mode 100644 index dc9525a8..00000000 --- a/src/core.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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include "core.h" -#include "module.h" -#include "sink.h" -#include "source.h" -#include "namereg.h" -#include "util.h" - -struct pa_core* pa_core_new(struct pa_mainloop_api *m) { - struct pa_core* c; - c = malloc(sizeof(struct pa_core)); - assert(c); - - c->mainloop = m; - c->clients = pa_idxset_new(NULL, NULL); - c->sinks = pa_idxset_new(NULL, NULL); - c->sources = pa_idxset_new(NULL, NULL); - c->source_outputs = pa_idxset_new(NULL, NULL); - c->sink_inputs = pa_idxset_new(NULL, NULL); - - c->default_source_index = c->default_sink_index = PA_IDXSET_INVALID; - - c->modules = NULL; - c->namereg = NULL; - - c->default_sample_spec.format = PA_SAMPLE_S16NE; - c->default_sample_spec.rate = 44100; - c->default_sample_spec.channels = 2; - - pa_check_for_sigpipe(); - - return c; -}; - -void pa_core_free(struct pa_core *c) { - assert(c); - - pa_module_unload_all(c); - assert(!c->modules); - - assert(pa_idxset_isempty(c->clients)); - pa_idxset_free(c->clients, NULL, NULL); - - assert(pa_idxset_isempty(c->sinks)); - pa_idxset_free(c->sinks, NULL, NULL); - - assert(pa_idxset_isempty(c->sources)); - pa_idxset_free(c->sources, NULL, NULL); - - assert(pa_idxset_isempty(c->source_outputs)); - pa_idxset_free(c->source_outputs, NULL, NULL); - - assert(pa_idxset_isempty(c->sink_inputs)); - pa_idxset_free(c->sink_inputs, NULL, NULL); - - pa_namereg_free(c); - - free(c); -}; - diff --git a/src/core.h b/src/core.h deleted file mode 100644 index 99d7d76a..00000000 --- a/src/core.h +++ /dev/null @@ -1,45 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "idxset.h" -#include "hashmap.h" -#include "mainloop-api.h" -#include "sample.h" - -struct pa_core { - struct pa_mainloop_api *mainloop; - - struct pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules; - - struct pa_hashmap *namereg; - - uint32_t default_source_index, default_sink_index; - - struct pa_sample_spec default_sample_spec; -}; - -struct pa_core* pa_core_new(struct pa_mainloop_api *m); -void pa_core_free(struct pa_core*c); - -#endif diff --git a/src/depmod.py b/src/depmod.py deleted file mode 100755 index 85dc66a8..00000000 --- a/src/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 General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# polypaudio is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with polypaudio; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA. - -import sys, os, string - -exported_symbols = {} -imported_symbols = {} - -for fn in sys.argv[1:]: - f = os.popen("nm '%s'" % fn, "r") - - imported_symbols[fn] = [] - - for line in f: - sym_address = line[:7].strip() - sym_type = line[9].strip() - sym_name = line[11:].strip() - - if sym_name in ('_fini', '_init'): - continue - - if sym_type in ('T', 'B', 'R', 'D' 'G', 'S', 'D'): - if exported_symbols.has_key(sym_name): - sys.stderr.write("CONFLICT: %s defined in both '%s' and '%s'.\n" % (sym_name, fn, exported_symbols[sym_name])) - else: - exported_symbols[sym_name] = fn - elif sym_type in ('U',): - if sym_name[:3] == 'pa_': - imported_symbols[fn].append(sym_name) - - f.close() - -dependencies = {} -unresolved_symbols = {} - -for fn in imported_symbols: - dependencies[fn] = [] - - for sym in imported_symbols[fn]: - if exported_symbols.has_key(sym): - if exported_symbols[sym] not in dependencies[fn]: - dependencies[fn].append(exported_symbols[sym]) - else: - if unresolved_symbols.has_key(sym): - unresolved_symbols[sym].append(fn) - else: - unresolved_symbols[sym] = [fn] - -for sym, files in unresolved_symbols.iteritems(): - print "WARNING: Unresolved symbol '%s' in %s" % (sym, `files`) - -k = dependencies.keys() -k.sort() -for fn in k: - dependencies[fn].sort() - print "%s: %s" % (fn, string.join(dependencies[fn], " ")) diff --git a/src/dynarray.c b/src/dynarray.c deleted file mode 100644 index 24306964..00000000 --- a/src/dynarray.c +++ /dev/null @@ -1,98 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include "dynarray.h" - -struct pa_dynarray { - void **data; - unsigned n_allocated, n_entries; -}; - -struct pa_dynarray* pa_dynarray_new(void) { - struct pa_dynarray *a; - a = malloc(sizeof(struct pa_dynarray)); - assert(a); - a->data = NULL; - a->n_entries = 0; - a->n_allocated = 0; - return a; -} - -void pa_dynarray_free(struct pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata) { - unsigned i; - assert(a); - - if (func) - for (i = 0; i < a->n_entries; i++) - if (a->data[i]) - func(a->data[i], userdata); - - free(a->data); - free(a); -} - -void pa_dynarray_put(struct pa_dynarray*a, unsigned i, void *p) { - assert(a); - - if (i >= a->n_allocated) { - unsigned n; - - if (!p) - return; - - n = i+100; - a->data = realloc(a->data, sizeof(void*)*n); - memset(a->data+a->n_allocated, 0, sizeof(void*)*(n-a->n_allocated)); - a->n_allocated = n; - } - - a->data[i] = p; - - if (i >= a->n_entries) - a->n_entries = i+1; -} - -unsigned pa_dynarray_append(struct pa_dynarray*a, void *p) { - unsigned i = a->n_entries; - pa_dynarray_put(a, i, p); - return i; -} - -void *pa_dynarray_get(struct pa_dynarray*a, unsigned i) { - assert(a); - if (i >= a->n_allocated) - return NULL; - assert(a->data); - return a->data[i]; -} - -unsigned pa_dynarray_ncontents(struct pa_dynarray*a) { - assert(a); - return a->n_entries; -} diff --git a/src/dynarray.h b/src/dynarray.h deleted file mode 100644 index 56ec5386..00000000 --- a/src/dynarray.h +++ /dev/null @@ -1,37 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -struct pa_dynarray; - -struct pa_dynarray* pa_dynarray_new(void); -void pa_dynarray_free(struct pa_dynarray* a, void (*func)(void *p, void *userdata), void *userdata); - -void pa_dynarray_put(struct pa_dynarray*a, unsigned i, void *p); -unsigned pa_dynarray_append(struct pa_dynarray*a, void *p); - -void *pa_dynarray_get(struct pa_dynarray*a, unsigned i); - -unsigned pa_dynarray_ncontents(struct pa_dynarray*a); - -#endif diff --git a/src/endianmacros.h b/src/endianmacros.h deleted file mode 100644 index cd7b7d83..00000000 --- a/src/endianmacros.h +++ /dev/null @@ -1,62 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -#ifdef HAVE_CONFIG_H -#include -#endif - -#define INT16_SWAP(x) ((int16_t)(((int16_t) x >> 8) | ((int16_t) x << 8))) -#define UINT16_SWAP(x) ((uint16_t)(((uint16_t) x >> 8) | ((uint16_t) x << 8))) -#define INT32_SWAP(x) ((int32_t)(((int32_t) x >> 24) | ((int32_t) x << 24) | (((int32_t) x & 0xFF00) << 16) | (((int32_t) x) >> 16) & 0xFF00)) -#define UINT32_SWAP(x) ((uint32_t)(((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 16) | (((uint32_t) x) >> 16) & 0xFF00)) - -#ifdef WORDS_BIGENDIAN - #define INT16_FROM_LE(x) INT16_SWAP(x) - #define INT16_FROM_BE(x) ((int16_t)(x)) - #define INT16_TO_LE(x) INT16_SWAP(x) - #define INT16_TO_BE(x) ((int16_t)(x)) - - #define UINT16_FROM_LE(x) UINT16_SWAP(x) - #define UINT16_FROM_BE(x) ((uint16_t)(x)) - #define INT32_FROM_LE(x) INT32_SWAP(x) - #define INT32_FROM_BE(x) ((int32_t)(x)) - #define UINT32_FROM_LE(x) UINT32_SWAP(x) - #define UINT32_FROM_BE(x) ((uint32_t)(x)) -#else - #define INT16_FROM_LE(x) ((int16_t)(x)) - #define INT16_FROM_BE(x) INT16_SWAP(x) - #define INT16_TO_LE(x) ((int16_t)(x)) - #define INT16_TO_BE(x) INT16_SWAP(x) - - #define UINT16_FROM_LE(x) ((uint16_t)(x)) - #define UINT16_FROM_BE(x) UINT16_SWAP(x) - #define INT32_FROM_LE(x) ((int32_t)(x)) - #define INT32_FROM_BE(x) INT32_SWAP(x) - #define UINT32_FROM_LE(x) ((uint32_t)(x)) - #define UINT32_FROM_BE(x) UINT32_SWAP(x) -#endif - -#endif diff --git a/src/esound.h b/src/esound.h deleted file mode 100644 index 01a2962f..00000000 --- a/src/esound.h +++ /dev/null @@ -1,213 +0,0 @@ -#ifndef fooesoundhfoo -#define fooesoundhfoo - -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -/* Most of the following is blatantly stolen from esound. */ - - -/* path and name of the default EsounD domain socket */ -#define ESD_UNIX_SOCKET_DIR "/tmp/.esd" -#define ESD_UNIX_SOCKET_NAME "/tmp/.esd/socket" - -/* length of the audio buffer size */ -#define ESD_BUF_SIZE (4 * 1024) -/* maximum size we can write(). Otherwise we might overflow */ -#define ESD_MAX_WRITE_SIZE (21 * 4096) - -/* length of the authorization key, octets */ -#define ESD_KEY_LEN (16) - -/* default port for the EsounD server */ -#define ESD_DEFAULT_PORT (16001) - -/* default sample rate for the EsounD server */ -#define ESD_DEFAULT_RATE (44100) - -/* maximum length of a stream/sample name */ -#define ESD_NAME_MAX (128) - -/* a magic number to identify the relative endianness of a client */ -#define ESD_ENDIAN_KEY ((uint32_t) (('E' << 24) + ('N' << 16) + ('D' << 8) + ('N'))) - -#define ESD_VOLUME_BASE (256) - - -/*************************************/ -/* what can we do to/with the EsounD */ -enum esd_proto { - ESD_PROTO_CONNECT, /* implied on inital client connection */ - - /* pseudo "security" functionality */ - ESD_PROTO_LOCK, /* disable "foreign" client connections */ - ESD_PROTO_UNLOCK, /* enable "foreign" client connections */ - - /* stream functionality: play, record, monitor */ - ESD_PROTO_STREAM_PLAY, /* play all following data as a stream */ - ESD_PROTO_STREAM_REC, /* record data from card as a stream */ - ESD_PROTO_STREAM_MON, /* send mixed buffer output as a stream */ - - /* sample functionality: cache, free, play, loop, EOL, kill */ - ESD_PROTO_SAMPLE_CACHE, /* cache a sample in the server */ - ESD_PROTO_SAMPLE_FREE, /* release a sample in the server */ - ESD_PROTO_SAMPLE_PLAY, /* play a cached sample */ - ESD_PROTO_SAMPLE_LOOP, /* loop a cached sample, til eoloop */ - ESD_PROTO_SAMPLE_STOP, /* stop a looping sample when done */ - ESD_PROTO_SAMPLE_KILL, /* stop the looping sample immed. */ - - /* free and reclaim /dev/dsp functionality */ - ESD_PROTO_STANDBY, /* release /dev/dsp and ignore all data */ - ESD_PROTO_RESUME, /* reclaim /dev/dsp and play sounds again */ - - /* TODO: move these to a more logical place. NOTE: will break the protocol */ - ESD_PROTO_SAMPLE_GETID, /* get the ID for an already-cached sample */ - ESD_PROTO_STREAM_FILT, /* filter mixed buffer output as a stream */ - - /* esd remote management */ - ESD_PROTO_SERVER_INFO, /* get server info (ver, sample rate, format) */ - ESD_PROTO_ALL_INFO, /* get all info (server info, players, samples) */ - ESD_PROTO_SUBSCRIBE, /* track new and removed players and samples */ - ESD_PROTO_UNSUBSCRIBE, /* stop tracking updates */ - - /* esd remote control */ - ESD_PROTO_STREAM_PAN, /* set stream panning */ - ESD_PROTO_SAMPLE_PAN, /* set default sample panning */ - - /* esd status */ - ESD_PROTO_STANDBY_MODE, /* see if server is in standby, autostandby, etc */ - - /* esd latency */ - ESD_PROTO_LATENCY, /* retrieve latency between write()'s and output */ - - ESD_PROTO_MAX /* for bounds checking */ -}; - -/******************/ -/* The EsounD api */ - -/* the properties of a sound buffer are logically or'd */ - -/* bits of stream/sample data */ -#define ESD_MASK_BITS ( 0x000F ) -#define ESD_BITS8 ( 0x0000 ) -#define ESD_BITS16 ( 0x0001 ) - -/* how many interleaved channels of data */ -#define ESD_MASK_CHAN ( 0x00F0 ) -#define ESD_MONO ( 0x0010 ) -#define ESD_STEREO ( 0x0020 ) - -/* whether it's a stream or a sample */ -#define ESD_MASK_MODE ( 0x0F00 ) -#define ESD_STREAM ( 0x0000 ) -#define ESD_SAMPLE ( 0x0100 ) -#define ESD_ADPCM ( 0x0200 ) /* TODO: anyone up for this? =P */ - -/* the function of the stream/sample, and common functions */ -#define ESD_MASK_FUNC ( 0xF000 ) -#define ESD_PLAY ( 0x1000 ) -/* functions for streams only */ -#define ESD_MONITOR ( 0x0000 ) -#define ESD_RECORD ( 0x2000 ) -/* functions for samples only */ -#define ESD_STOP ( 0x0000 ) -#define ESD_LOOP ( 0x2000 ) - -typedef int esd_format_t; -typedef int esd_proto_t; - -/*******************************************************************/ -/* esdmgr.c - functions to implement a "sound manager" for esd */ - -/* structures to retrieve information about streams/samples from the server */ -typedef struct esd_server_info { - - int version; /* server version encoded as an int */ - esd_format_t format; /* magic int with the format info */ - int rate; /* sample rate */ - -} esd_server_info_t; - -typedef struct esd_player_info { - - struct esd_player_info *next; /* point to next entry in list */ - esd_server_info_t *server; /* the server that contains this stream */ - - int source_id; /* either a stream fd or sample id */ - char name[ ESD_NAME_MAX ]; /* name of stream for remote control */ - int rate; /* sample rate */ - int left_vol_scale; /* volume scaling */ - int right_vol_scale; - - esd_format_t format; /* magic int with the format info */ - -} esd_player_info_t; - -typedef struct esd_sample_info { - - struct esd_sample_info *next; /* point to next entry in list */ - esd_server_info_t *server; /* the server that contains this sample */ - - int sample_id; /* either a stream fd or sample id */ - char name[ ESD_NAME_MAX ]; /* name of stream for remote control */ - int rate; /* sample rate */ - int left_vol_scale; /* volume scaling */ - int right_vol_scale; - - esd_format_t format; /* magic int with the format info */ - int length; /* total buffer length */ - -} esd_sample_info_t; - -typedef struct esd_info { - - esd_server_info_t *server; - esd_player_info_t *player_list; - esd_sample_info_t *sample_list; - -} esd_info_t; - -enum esd_standby_mode { - ESM_ERROR, ESM_ON_STANDBY, ESM_ON_AUTOSTANDBY, ESM_RUNNING -}; -typedef int esd_standby_mode_t; - -enum esd_client_state { - ESD_STREAMING_DATA, /* data from here on is streamed data */ - ESD_CACHING_SAMPLE, /* midway through caching a sample */ - ESD_NEEDS_REQDATA, /* more data needed to complere request */ - ESD_NEXT_REQUEST, /* proceed to next request */ - ESD_CLIENT_STATE_MAX /* place holder */ -}; -typedef int esd_client_state_t; - -/* switch endian order for cross platform playing */ -#define swap_endian_32(x) ((x >> 24) | ((x >> 8) & 0xFF00) | (((x & 0xFF00) << 8)) | (x << 24)) -#define maybe_swap_endian_32(c,x) ((c) ? swap_endian_32(x) : x) - -/* the endian key is transferred in binary, if it's read into int, */ -/* and matches ESD_ENDIAN_KEY (ENDN), then the endianness of the */ -/* server and the client match; if it's SWAP_ENDIAN_KEY, swap data */ -#define ESD_SWAP_ENDIAN_KEY ((uint32_t) swap_endian_32(ESD_ENDIAN_KEY)) - - -#endif diff --git a/src/hashmap.c b/src/hashmap.c deleted file mode 100644 index 2c7c92b5..00000000 --- a/src/hashmap.c +++ /dev/null @@ -1,170 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include "hashmap.h" -#include "idxset.h" - -struct hashmap_entry { - struct hashmap_entry *next, *previous, *bucket_next, *bucket_previous; - unsigned hash; - const void *key; - void *value; -}; - -struct pa_hashmap { - unsigned size; - struct hashmap_entry **data; - struct hashmap_entry *first_entry; - - unsigned n_entries; - unsigned (*hash_func) (const void *p); - int (*compare_func) (const void*a, const void*b); -}; - -struct pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) { - struct pa_hashmap *h; - h = malloc(sizeof(struct pa_hashmap)); - assert(h); - h->data = malloc(sizeof(struct hashmap_entry*)*(h->size = 1023)); - assert(h->data); - memset(h->data, 0, sizeof(struct hashmap_entry*)*(h->size = 1023)); - h->first_entry = NULL; - h->n_entries = 0; - h->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func; - h->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func; - return h; -} - -static void remove(struct pa_hashmap *h, struct hashmap_entry *e) { - assert(e); - - if (e->next) - e->next->previous = e->previous; - if (e->previous) - e->previous->next = e->next; - else - h->first_entry = e->next; - - if (e->bucket_next) - e->bucket_next->bucket_previous = e->bucket_previous; - if (e->bucket_previous) - e->bucket_previous->bucket_next = e->bucket_next; - else - h->data[e->hash] = e->bucket_next; - - free(e); - h->n_entries--; -} - -void pa_hashmap_free(struct pa_hashmap*h, void (*free_func)(void *p, void *userdata), void *userdata) { - assert(h); - - while (h->first_entry) { - if (free_func) - free_func(h->first_entry->value, userdata); - remove(h, h->first_entry); - } - - free(h->data); - free(h); -} - -static struct hashmap_entry *get(struct pa_hashmap *h, unsigned hash, const void *key) { - struct hashmap_entry *e; - - for (e = h->data[hash]; e; e = e->bucket_next) - if (h->compare_func(e->key, key) == 0) - return e; - - return NULL; -} - -int pa_hashmap_put(struct pa_hashmap *h, const void *key, void *value) { - struct hashmap_entry *e; - unsigned hash; - assert(h && key); - - hash = h->hash_func(key) % h->size; - - if ((e = get(h, hash, key))) - return -1; - - e = malloc(sizeof(struct hashmap_entry)); - assert(e); - - e->hash = hash; - e->key = key; - e->value = value; - - e->previous = NULL; - e->next = h->first_entry; - if (h->first_entry) - h->first_entry->previous = e; - h->first_entry = e; - - e->bucket_previous = NULL; - e->bucket_next = h->data[hash]; - if (h->data[hash]) - h->data[hash]->bucket_previous = e; - h->data[hash] = e; - - h->n_entries ++; - return 0; -} - -void* pa_hashmap_get(struct pa_hashmap *h, const void *key) { - unsigned hash; - struct hashmap_entry *e; - assert(h && key); - - hash = h->hash_func(key) % h->size; - - if (!(e = get(h, hash, key))) - return NULL; - - return e->value; -} - -int pa_hashmap_remove(struct pa_hashmap *h, const void *key) { - struct hashmap_entry *e; - unsigned hash; - assert(h && key); - - hash = h->hash_func(key) % h->size; - - if (!(e = get(h, hash, key))) - return 1; - - remove(h, e); - return 0; -} - -unsigned pa_hashmap_ncontents(struct pa_hashmap *h) { - return h->n_entries; -} diff --git a/src/hashmap.h b/src/hashmap.h deleted file mode 100644 index b24e74a5..00000000 --- a/src/hashmap.h +++ /dev/null @@ -1,37 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -struct pa_hashmap; - -struct pa_hashmap *pa_hashmap_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)); -void pa_hashmap_free(struct pa_hashmap*, void (*free_func)(void *p, void *userdata), void *userdata); - -int pa_hashmap_put(struct pa_hashmap *h, const void *key, void *value); -void* pa_hashmap_get(struct pa_hashmap *h, const void *key); - -int pa_hashmap_remove(struct pa_hashmap *h, const void *key); - -unsigned pa_hashmap_ncontents(struct pa_hashmap *h); - -#endif diff --git a/src/idxset.c b/src/idxset.c deleted file mode 100644 index cecda6b7..00000000 --- a/src/idxset.c +++ /dev/null @@ -1,397 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include "idxset.h" - -struct idxset_entry { - void *data; - uint32_t index; - unsigned hash_value; - - struct idxset_entry *hash_prev, *hash_next; - struct idxset_entry* iterate_prev, *iterate_next; -}; - -struct pa_idxset { - unsigned (*hash_func) (const void *p); - int (*compare_func)(const void *a, const void *b); - - unsigned hash_table_size, n_entries; - struct idxset_entry **hash_table, **array, *iterate_list_head, *iterate_list_tail; - uint32_t index, start_index, array_size; -}; - -unsigned pa_idxset_string_hash_func(const void *p) { - unsigned hash = 0; - const char *c; - - for (c = p; *c; c++) - hash = 31 * hash + *c; - - return hash; -} - -int pa_idxset_string_compare_func(const void *a, const void *b) { - return strcmp(a, b); -} - -unsigned pa_idxset_trivial_hash_func(const void *p) { - return (unsigned) p; -} - -int pa_idxset_trivial_compare_func(const void *a, const void *b) { - return a != b; -} - -struct pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)) { - struct pa_idxset *s; - - s = malloc(sizeof(struct pa_idxset)); - assert(s); - s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func; - s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func; - s->hash_table_size = 1023; - s->hash_table = malloc(sizeof(struct idxset_entry*)*s->hash_table_size); - assert(s->hash_table); - memset(s->hash_table, 0, sizeof(struct idxset_entry*)*s->hash_table_size); - s->array = NULL; - s->array_size = 0; - s->index = 0; - s->start_index = 0; - s->n_entries = 0; - - s->iterate_list_head = s->iterate_list_tail = NULL; - - return s; -} - -void pa_idxset_free(struct pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata) { - assert(s); - - if (free_func) { - while (s->iterate_list_head) { - struct idxset_entry *e = s->iterate_list_head; - s->iterate_list_head = s->iterate_list_head->iterate_next; - - if (free_func) - free_func(e->data, userdata); - free(e); - } - } - - free(s->hash_table); - free(s->array); - free(s); -} - -static struct idxset_entry* hash_scan(struct pa_idxset *s, struct idxset_entry* e, void *p) { - assert(p); - - assert(s->compare_func); - for (; e; e = e->hash_next) - if (s->compare_func(e->data, p) == 0) - return e; - - return NULL; -} - -static void extend_array(struct pa_idxset *s, uint32_t index) { - uint32_t i, j, l; - struct idxset_entry** n; - assert(index >= s->start_index); - - if (index < s->start_index + s->array_size) - return; - - for (i = 0; i < s->array_size; i++) - if (s->array[i]) - break; - - l = index - s->start_index - i + 100; - n = malloc(sizeof(struct hash_table_entry*)*l); - assert(n); - memset(n, 0, sizeof(struct hash_table_entry*)*l); - - for (j = 0; j < s->array_size-i; j++) - n[j] = s->array[i+j]; - - free(s->array); - - s->array = n; - s->array_size = l; - s->start_index += i; -} - -static struct idxset_entry** array_index(struct pa_idxset*s, uint32_t index) { - if (index >= s->start_index + s->array_size) - return NULL; - - if (index < s->start_index) - return NULL; - - return s->array + (index - s->start_index); -} - -int pa_idxset_put(struct pa_idxset*s, void *p, uint32_t *index) { - unsigned h; - struct idxset_entry *e, **a; - assert(s && p); - - assert(s->hash_func); - h = s->hash_func(p) % s->hash_table_size; - - assert(s->hash_table); - if ((e = hash_scan(s, s->hash_table[h], p))) { - if (index) - *index = e->index; - - return -1; - } - - e = malloc(sizeof(struct idxset_entry)); - assert(e); - - e->data = p; - e->index = s->index++; - e->hash_value = h; - - /* Insert into hash table */ - e->hash_next = s->hash_table[h]; - e->hash_prev = NULL; - if (s->hash_table[h]) - s->hash_table[h]->hash_prev = e; - s->hash_table[h] = e; - - /* Insert into array */ - extend_array(s, e->index); - a = array_index(s, e->index); - assert(a && !*a); - *a = e; - - /* Insert into linked list */ - e->iterate_next = NULL; - e->iterate_prev = s->iterate_list_tail; - if (s->iterate_list_tail) { - assert(s->iterate_list_head); - s->iterate_list_tail->iterate_next = e; - } else { - assert(!s->iterate_list_head); - s->iterate_list_head = e; - } - s->iterate_list_tail = e; - - s->n_entries++; - assert(s->n_entries >= 1); - - if (index) - *index = e->index; - - return 0; -} - -void* pa_idxset_get_by_index(struct pa_idxset*s, uint32_t index) { - struct idxset_entry **a; - assert(s); - - if (!(a = array_index(s, index))) - return NULL; - - if (!*a) - return NULL; - - return (*a)->data; -} - -void* pa_idxset_get_by_data(struct pa_idxset*s, void *p, uint32_t *index) { - unsigned h; - struct idxset_entry *e; - assert(s && p); - - assert(s->hash_func); - h = s->hash_func(p) % s->hash_table_size; - - assert(s->hash_table); - if (!(e = hash_scan(s, s->hash_table[h], p))) - return NULL; - - if (index) - *index = e->index; - - return e->data; -} - -static void remove_entry(struct pa_idxset *s, struct idxset_entry *e) { - struct idxset_entry **a; - assert(s && e); - - /* Remove from array */ - a = array_index(s, e->index); - assert(a && *a && *a == e); - *a = NULL; - - /* Remove from linked list */ - if (e->iterate_next) - e->iterate_next->iterate_prev = e->iterate_prev; - else - s->iterate_list_tail = e->iterate_prev; - - if (e->iterate_prev) - e->iterate_prev->iterate_next = e->iterate_next; - else - s->iterate_list_head = e->iterate_next; - - /* Remove from hash table */ - if (e->hash_next) - e->hash_next->hash_prev = e->hash_prev; - - if (e->hash_prev) - e->hash_prev->hash_next = e->hash_next; - else - s->hash_table[e->hash_value] = e->hash_next; - - free(e); - - assert(s->n_entries >= 1); - s->n_entries--; -} - -void* pa_idxset_remove_by_index(struct pa_idxset*s, uint32_t index) { - struct idxset_entry **a; - void *data; - - assert(s); - - if (!(a = array_index(s, index))) - return NULL; - - data = (*a)->data; - remove_entry(s, *a); - - return data; -} - -void* pa_idxset_remove_by_data(struct pa_idxset*s, void *data, uint32_t *index) { - struct idxset_entry *e; - unsigned h; - - assert(s->hash_func); - h = s->hash_func(data) % s->hash_table_size; - - assert(s->hash_table); - if (!(e = hash_scan(s, s->hash_table[h], data))) - return NULL; - - data = e->data; - if (index) - *index = e->index; - - remove_entry(s, e); - - return data; -} - -void* pa_idxset_rrobin(struct pa_idxset *s, uint32_t *index) { - struct idxset_entry **a, *e = NULL; - assert(s && index); - - if ((a = array_index(s, *index)) && *a) - e = (*a)->iterate_next; - - if (!e) - e = s->iterate_list_head; - - if (!e) - return NULL; - - *index = e->index; - return e->data; -} - -void* pa_idxset_first(struct pa_idxset *s, uint32_t *index) { - assert(s); - - if (!s->iterate_list_head) - return NULL; - - if (index) - *index = s->iterate_list_head->index; - return s->iterate_list_head->data; -} - -void *pa_idxset_next(struct pa_idxset *s, uint32_t *index) { - struct idxset_entry **a, *e = NULL; - assert(s && index); - - if ((a = array_index(s, *index)) && *a) - e = (*a)->iterate_next; - - if (e) { - *index = e->index; - return e->data; - } else { - *index = PA_IDXSET_INVALID; - return NULL; - } -} - - -int pa_idxset_foreach(struct pa_idxset*s, int (*func)(void *p, uint32_t index, int *del, void*userdata), void *userdata) { - struct idxset_entry *e; - assert(s && func); - - e = s->iterate_list_head; - while (e) { - int del = 0, r; - struct idxset_entry *n = e->iterate_next; - - r = func(e->data, e->index, &del, userdata); - - if (del) - remove_entry(s, e); - - if (r < 0) - return r; - - e = n; - } - - return 0; -} - -unsigned pa_idxset_ncontents(struct pa_idxset*s) { - assert(s); - return s->n_entries; -} - -int pa_idxset_isempty(struct pa_idxset *s) { - assert(s); - return s->n_entries == 0; -} - diff --git a/src/idxset.h b/src/idxset.h deleted file mode 100644 index f26b03fb..00000000 --- a/src/idxset.h +++ /dev/null @@ -1,63 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -#define PA_IDXSET_INVALID ((uint32_t) -1) - -unsigned pa_idxset_trivial_hash_func(const void *p); -int pa_idxset_trivial_compare_func(const void *a, const void *b); - -unsigned pa_idxset_string_hash_func(const void *p); -int pa_idxset_string_compare_func(const void *a, const void *b); - -struct pa_idxset; - -struct pa_idxset* pa_idxset_new(unsigned (*hash_func) (const void *p), int (*compare_func) (const void*a, const void*b)); -void pa_idxset_free(struct pa_idxset *s, void (*free_func) (void *p, void *userdata), void *userdata); - -int pa_idxset_put(struct pa_idxset*s, void *p, uint32_t *index); - -void* pa_idxset_get_by_index(struct pa_idxset*s, uint32_t index); -void* pa_idxset_get_by_data(struct pa_idxset*s, void *p, uint32_t *index); - -void* pa_idxset_remove_by_index(struct pa_idxset*s, uint32_t index); -void* pa_idxset_remove_by_data(struct pa_idxset*s, void *p, uint32_t *index); - -/* This may be used to iterate through all entries. When called with - an invalid index value it returns the first entry, otherwise the - next following. The function is best called with *index = - PA_IDXSET_VALID first. */ -void* pa_idxset_rrobin(struct pa_idxset *s, uint32_t *index); - -/* Return the oldest entry in the idxset */ -void* pa_idxset_first(struct pa_idxset *s, uint32_t *index); -void *pa_idxset_next(struct pa_idxset *s, uint32_t *index); - -int pa_idxset_foreach(struct pa_idxset*s, int (*func)(void *p, uint32_t index, int *del, void*userdata), void *userdata); - -unsigned pa_idxset_ncontents(struct pa_idxset*s); -int pa_idxset_isempty(struct pa_idxset *s); - -#endif diff --git a/src/iochannel.c b/src/iochannel.c deleted file mode 100644 index 69d381f4..00000000 --- a/src/iochannel.c +++ /dev/null @@ -1,222 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include "iochannel.h" -#include "util.h" -#include "socket-util.h" - -struct pa_iochannel { - int ifd, ofd; - struct pa_mainloop_api* mainloop; - - void (*callback)(struct pa_iochannel*io, void *userdata); - void*userdata; - - int readable; - int writable; - int hungup; - - int no_close; - - void* input_source, *output_source; -}; - -static void enable_mainloop_sources(struct pa_iochannel *io) { - assert(io); - - if (io->input_source == io->output_source) { - enum pa_mainloop_api_io_events e = PA_MAINLOOP_API_IO_EVENT_NULL; - assert(io->input_source); - - if (!io->readable) - e |= PA_MAINLOOP_API_IO_EVENT_INPUT; - if (!io->writable) - e |= PA_MAINLOOP_API_IO_EVENT_OUTPUT; - - io->mainloop->enable_io(io->mainloop, io->input_source, e); - } else { - if (io->input_source) - io->mainloop->enable_io(io->mainloop, io->input_source, io->readable ? PA_MAINLOOP_API_IO_EVENT_NULL : PA_MAINLOOP_API_IO_EVENT_INPUT); - if (io->output_source) - io->mainloop->enable_io(io->mainloop, io->output_source, io->writable ? PA_MAINLOOP_API_IO_EVENT_NULL : PA_MAINLOOP_API_IO_EVENT_OUTPUT); - } -} - -static void callback(struct pa_mainloop_api* m, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { - struct pa_iochannel *io = userdata; - int changed = 0; - assert(m && fd >= 0 && events && userdata); - - if ((events & PA_MAINLOOP_API_IO_EVENT_HUP) && !io->hungup) { - io->hungup = 1; - changed = 1; - } - - if ((events & PA_MAINLOOP_API_IO_EVENT_INPUT) && !io->readable) { - io->readable = 1; - changed = 1; - assert(id == io->input_source); - } - - if ((events & PA_MAINLOOP_API_IO_EVENT_OUTPUT) && !io->writable) { - io->writable = 1; - changed = 1; - assert(id == io->output_source); - } - - if (changed) { - enable_mainloop_sources(io); - - if (io->callback) - io->callback(io, io->userdata); - } -} - -struct pa_iochannel* pa_iochannel_new(struct pa_mainloop_api*m, int ifd, int ofd) { - struct pa_iochannel *io; - assert(m && (ifd >= 0 || ofd >= 0)); - - io = malloc(sizeof(struct pa_iochannel)); - io->ifd = ifd; - io->ofd = ofd; - io->mainloop = m; - - io->userdata = NULL; - io->callback = NULL; - io->readable = 0; - io->writable = 0; - io->hungup = 0; - io->no_close = 0; - - if (ifd == ofd) { - assert(ifd >= 0); - pa_make_nonblock_fd(io->ifd); - io->input_source = io->output_source = m->source_io(m, ifd, PA_MAINLOOP_API_IO_EVENT_BOTH, callback, io); - } else { - - if (ifd >= 0) { - pa_make_nonblock_fd(io->ifd); - io->input_source = m->source_io(m, ifd, PA_MAINLOOP_API_IO_EVENT_INPUT, callback, io); - } else - io->input_source = NULL; - - if (ofd >= 0) { - pa_make_nonblock_fd(io->ofd); - io->output_source = m->source_io(m, ofd, PA_MAINLOOP_API_IO_EVENT_OUTPUT, callback, io); - } else - io->output_source = NULL; - } - - return io; -} - -void pa_iochannel_free(struct pa_iochannel*io) { - assert(io); - - if (!io->no_close) { - if (io->ifd >= 0) - close(io->ifd); - if (io->ofd >= 0 && io->ofd != io->ifd) - close(io->ofd); - } - - if (io->input_source) - io->mainloop->cancel_io(io->mainloop, io->input_source); - if (io->output_source && (io->output_source != io->input_source)) - io->mainloop->cancel_io(io->mainloop, io->output_source); - - free(io); -} - -int pa_iochannel_is_readable(struct pa_iochannel*io) { - assert(io); - return io->readable; -} - -int pa_iochannel_is_writable(struct pa_iochannel*io) { - assert(io); - return io->writable; -} - -int pa_iochannel_is_hungup(struct pa_iochannel*io) { - assert(io); - return io->hungup; -} - -ssize_t pa_iochannel_write(struct pa_iochannel*io, const void*data, size_t l) { - ssize_t r; - assert(io && data && l && io->ofd >= 0); - - if ((r = write(io->ofd, data, l)) >= 0) { - io->writable = 0; - enable_mainloop_sources(io); - } - - return r; -} - -ssize_t pa_iochannel_read(struct pa_iochannel*io, void*data, size_t l) { - ssize_t r; - - assert(io && data && io->ifd >= 0); - - if ((r = read(io->ifd, data, l)) >= 0) { - io->readable = 0; - enable_mainloop_sources(io); - } - - return r; -} - -void pa_iochannel_set_callback(struct pa_iochannel*io, void (*callback)(struct pa_iochannel*io, void *userdata), void *userdata) { - assert(io); - io->callback = callback; - io->userdata = userdata; -} - -void pa_iochannel_set_noclose(struct pa_iochannel*io, int b) { - assert(io); - io->no_close = b; -} - -void pa_iochannel_socket_peer_to_string(struct pa_iochannel*io, char*s, size_t l) { - assert(io && s && l); - pa_socket_peer_to_string(io->ifd, s, l); -} - -int pa_iochannel_socket_set_rcvbuf(struct pa_iochannel *io, size_t l) { - assert(io); - return pa_socket_set_rcvbuf(io->ifd, l); -} - -int pa_iochannel_socket_set_sndbuf(struct pa_iochannel *io, size_t l) { - assert(io); - return pa_socket_set_sndbuf(io->ofd, l); -} diff --git a/src/iochannel.h b/src/iochannel.h deleted file mode 100644 index 6f5f351c..00000000 --- a/src/iochannel.h +++ /dev/null @@ -1,50 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include "mainloop-api.h" - -/* It is safe to destroy the calling iochannel object from the callback */ - -struct pa_iochannel; - -struct pa_iochannel* pa_iochannel_new(struct pa_mainloop_api*m, int ifd, int ofd); -void pa_iochannel_free(struct pa_iochannel*io); - -ssize_t pa_iochannel_write(struct pa_iochannel*io, const void*data, size_t l); -ssize_t pa_iochannel_read(struct pa_iochannel*io, void*data, size_t l); - -int pa_iochannel_is_readable(struct pa_iochannel*io); -int pa_iochannel_is_writable(struct pa_iochannel*io); -int pa_iochannel_is_hungup(struct pa_iochannel*io); - -void pa_iochannel_set_noclose(struct pa_iochannel*io, int b); - -void pa_iochannel_set_callback(struct pa_iochannel*io, void (*callback)(struct pa_iochannel*io, void *userdata), void *userdata); - -void pa_iochannel_socket_peer_to_string(struct pa_iochannel*io, char*s, size_t l); -int pa_iochannel_socket_set_rcvbuf(struct pa_iochannel*io, size_t l); -int pa_iochannel_socket_set_sndbuf(struct pa_iochannel*io, size_t l); - -#endif diff --git a/src/ioline.c b/src/ioline.c deleted file mode 100644 index ff9a03c2..00000000 --- a/src/ioline.c +++ /dev/null @@ -1,220 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include - -#include "ioline.h" - -#define BUFFER_LIMIT (64*1024) -#define READ_SIZE (1024) - -struct pa_ioline { - struct pa_iochannel *io; - int dead; - - char *wbuf; - size_t wbuf_length, wbuf_index, wbuf_valid_length; - - char *rbuf; - size_t rbuf_length, rbuf_index, rbuf_valid_length; - - void (*callback)(struct pa_ioline*io, const char *s, void *userdata); - void *userdata; -}; - -static void io_callback(struct pa_iochannel*io, void *userdata); -static int do_write(struct pa_ioline *l); - -struct pa_ioline* pa_ioline_new(struct pa_iochannel *io) { - struct pa_ioline *l; - assert(io); - - l = malloc(sizeof(struct pa_ioline)); - assert(l); - l->io = io; - l->dead = 0; - - l->wbuf = NULL; - l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0; - - l->rbuf = NULL; - l->rbuf_length = l->rbuf_index = l->rbuf_valid_length = 0; - - l->callback = NULL; - l->userdata = NULL; - - pa_iochannel_set_callback(io, io_callback, l); - - return l; -} - -void pa_ioline_free(struct pa_ioline *l) { - assert(l); - pa_iochannel_free(l->io); - free(l->wbuf); - free(l->rbuf); - free(l); -} - -void pa_ioline_puts(struct pa_ioline *l, const char *c) { - size_t len; - assert(l && c); - - len = strlen(c); - if (len > BUFFER_LIMIT - l->wbuf_valid_length) - len = BUFFER_LIMIT - l->wbuf_valid_length; - - if (!len) - return; - - if (len > l->wbuf_length - l->wbuf_valid_length) { - size_t n = l->wbuf_valid_length+len; - char *new = malloc(n); - if (l->wbuf) { - memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length); - free(l->wbuf); - } - l->wbuf = new; - l->wbuf_length = n; - l->wbuf_index = 0; - } else if (len > l->wbuf_length - l->wbuf_valid_length - l->wbuf_index) { - memmove(l->wbuf, l->wbuf+l->wbuf_index, l->wbuf_valid_length); - l->wbuf_index = 0; - } - - memcpy(l->wbuf+l->wbuf_index+l->wbuf_valid_length, c, len); - l->wbuf_valid_length += len; - - do_write(l); -} - -void pa_ioline_set_callback(struct pa_ioline*l, void (*callback)(struct pa_ioline*io, const char *s, void *userdata), void *userdata) { - assert(l && callback); - l->callback = callback; - l->userdata = userdata; -} - -static int do_read(struct pa_ioline *l) { - ssize_t r; - size_t m, len; - char *e; - assert(l); - - if (!pa_iochannel_is_readable(l->io)) - return 0; - - len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length; - - if (len < READ_SIZE) { - size_t n = l->rbuf_valid_length+READ_SIZE; - - if (n >= BUFFER_LIMIT) - n = BUFFER_LIMIT; - - if (l->rbuf_length >= n) { - if (l->rbuf_valid_length) - memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length); - } else { - char *new = malloc(n); - if (l->rbuf_valid_length) - memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length); - free(l->rbuf); - l->rbuf = new; - l->rbuf_length = n; - } - - l->rbuf_index = 0; - } - - len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length; - - if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0) - return -1; - - e = memchr(l->rbuf+l->rbuf_index+l->rbuf_valid_length, '\n', r); - l->rbuf_valid_length += r; - - if (!e &&l->rbuf_valid_length >= BUFFER_LIMIT) - e = l->rbuf+BUFFER_LIMIT-1; - - if (e) { - char *p; - - *e = 0; - - p = l->rbuf+l->rbuf_index; - m = strlen(p); - - l->rbuf_index += m+1; - l->rbuf_valid_length -= m+1; - - if (l->rbuf_valid_length == 0) - l->rbuf_index = 0; - - if (l->callback) - l->callback(l, p, l->userdata); - } - - return 0; -} - -static int do_write(struct pa_ioline *l) { - ssize_t r; - assert(l); - - if (!l->wbuf_valid_length || !pa_iochannel_is_writable(l->io)) - return 0; - - if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0) - return -1; - - l->wbuf_valid_length -= r; - if (l->wbuf_valid_length == 0) - l->wbuf_index = 0; - - return 0; -} - -static void io_callback(struct pa_iochannel*io, void *userdata) { - struct pa_ioline *l = userdata; - assert(io && l); - - if (!l->dead && do_write(l) < 0) - goto fail; - - if (!l->dead && do_read(l) < 0) - goto fail; - - return; - -fail: - l->dead = 1; - if (l->callback) - l->callback(l, NULL, l->userdata); -} diff --git a/src/ioline.h b/src/ioline.h deleted file mode 100644 index 5f29a16b..00000000 --- a/src/ioline.h +++ /dev/null @@ -1,35 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "iochannel.h" - -struct pa_ioline; - -struct pa_ioline* pa_ioline_new(struct pa_iochannel *io); -void pa_ioline_free(struct pa_ioline *l); - -void pa_ioline_puts(struct pa_ioline *s, const char *c); -void pa_ioline_set_callback(struct pa_ioline*io, void (*callback)(struct pa_ioline*io, const char *s, void *userdata), void *userdata); - -#endif diff --git a/src/main.c b/src/main.c deleted file mode 100644 index d9967cef..00000000 --- a/src/main.c +++ /dev/null @@ -1,182 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "core.h" -#include "mainloop.h" -#include "module.h" -#include "mainloop-signal.h" -#include "cmdline.h" -#include "cli-command.h" -#include "util.h" -#include "sioman.h" - -static struct pa_mainloop *mainloop; - -static void exit_signal_callback(void *id, int sig, void *userdata) { - struct pa_mainloop_api* m = pa_mainloop_get_api(mainloop); - m->quit(m, 1); - fprintf(stderr, __FILE__": got signal.\n"); -} - -static void aux_signal_callback(void *id, int sig, void *userdata) { - struct pa_core *c = userdata; - assert(c); - pa_module_load(c, sig == SIGUSR1 ? "module-cli" : "module-cli-protocol-unix", NULL); -} - -static void close_pipe(int p[2]) { - if (p[0] != -1) - close(p[0]); - if (p[1] != -1) - close(p[1]); - p[0] = p[1] = -1; -} - -int main(int argc, char *argv[]) { - struct pa_core *c; - struct pa_cmdline *cmdline = NULL; - struct pa_strbuf *buf = NULL; - char *s; - int r, retval = 1; - int daemon_pipe[2] = { -1, -1 }; - - if (!(cmdline = pa_cmdline_parse(argc, argv))) { - fprintf(stderr, __FILE__": failed to parse command line.\n"); - goto finish; - } - - if (cmdline->help) { - pa_cmdline_help(argv[0]); - retval = 0; - goto finish; - } - - if (cmdline->daemonize) { - pid_t child; - - if (pa_stdio_acquire() < 0) { - fprintf(stderr, __FILE__": failed to acquire stdio.\n"); - goto finish; - } - - if (pipe(daemon_pipe) < 0) { - fprintf(stderr, __FILE__": failed to create pipe.\n"); - goto finish; - } - - if ((child = fork()) < 0) { - fprintf(stderr, __FILE__": fork() failed: %s\n", strerror(errno)); - goto finish; - } - - if (child != 0) { - /* Father */ - - close(daemon_pipe[1]); - daemon_pipe[1] = -1; - - if (pa_loop_read(daemon_pipe[0], &retval, sizeof(retval)) != sizeof(retval)) { - fprintf(stderr, __FILE__": read() failed: %s\n", strerror(errno)); - retval = 1; - } - - goto finish; - } - - close(daemon_pipe[0]); - daemon_pipe[0] = -1; - - setsid(); - setpgrp(); - } - - r = lt_dlinit(); - assert(r == 0); - - mainloop = pa_mainloop_new(); - assert(mainloop); - - r = pa_signal_init(pa_mainloop_get_api(mainloop)); - assert(r == 0); - pa_signal_register(SIGINT, exit_signal_callback, NULL); - signal(SIGPIPE, SIG_IGN); - - c = pa_core_new(pa_mainloop_get_api(mainloop)); - assert(c); - - pa_signal_register(SIGUSR1, aux_signal_callback, c); - pa_signal_register(SIGUSR2, aux_signal_callback, c); - - buf = pa_strbuf_new(); - assert(buf); - r = pa_cli_command_execute(c, cmdline->cli_commands, buf, &cmdline->fail, &cmdline->verbose); - fprintf(stderr, s = pa_strbuf_tostring_free(buf)); - free(s); - - if (r < 0 && cmdline->fail) { - fprintf(stderr, __FILE__": failed to initialize daemon.\n"); - if (cmdline->daemonize) - pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); - } else if (!c->modules || pa_idxset_ncontents(c->modules) == 0) { - fprintf(stderr, __FILE__": daemon startup without any loaded modules, refusing to work.\n"); - if (cmdline->daemonize) - pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); - } else { - retval = 0; - if (cmdline->daemonize) - pa_loop_write(daemon_pipe[1], &retval, sizeof(retval)); - fprintf(stderr, __FILE__": mainloop entry.\n"); - if (pa_mainloop_run(mainloop, &retval) < 0) - retval = 1; - fprintf(stderr, __FILE__": mainloop exit.\n"); - } - - pa_core_free(c); - - pa_signal_done(); - pa_mainloop_free(mainloop); - - lt_dlexit(); - -finish: - - if (cmdline) - pa_cmdline_free(cmdline); - - close_pipe(daemon_pipe); - - return retval; -} diff --git a/src/mainloop-api.c b/src/mainloop-api.c deleted file mode 100644 index cce49c06..00000000 --- a/src/mainloop-api.c +++ /dev/null @@ -1,60 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include "mainloop-api.h" - -struct once_info { - void (*callback)(void *userdata); - void *userdata; -}; - -static void once_callback(struct pa_mainloop_api *api, void *id, void *userdata) { - struct once_info *i = userdata; - assert(api && i && i->callback); - i->callback(i->userdata); - assert(api->cancel_fixed); - api->cancel_fixed(api, id); - free(i); -} - -void pa_mainloop_api_once(struct pa_mainloop_api* api, void (*callback)(void *userdata), void *userdata) { - struct once_info *i; - void *id; - assert(api && callback); - - i = malloc(sizeof(struct once_info)); - assert(i); - i->callback = callback; - i->userdata = userdata; - - assert(api->source_fixed); - id = api->source_fixed(api, once_callback, i); - assert(id); - - /* Note: if the mainloop is destroyed before once_callback() was called, some memory is leaked. */ -} - diff --git a/src/mainloop-api.h b/src/mainloop-api.h deleted file mode 100644 index 0228f580..00000000 --- a/src/mainloop-api.h +++ /dev/null @@ -1,65 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include - -enum pa_mainloop_api_io_events { - PA_MAINLOOP_API_IO_EVENT_NULL = 0, - PA_MAINLOOP_API_IO_EVENT_INPUT = 1, - PA_MAINLOOP_API_IO_EVENT_OUTPUT = 2, - PA_MAINLOOP_API_IO_EVENT_BOTH = 3, - PA_MAINLOOP_API_IO_EVENT_HUP = 4 -}; - -struct pa_mainloop_api { - void *userdata; - - /* IO sources */ - void* (*source_io)(struct pa_mainloop_api*a, int fd, enum pa_mainloop_api_io_events events, void (*callback) (struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata); - void (*enable_io)(struct pa_mainloop_api*a, void* id, enum pa_mainloop_api_io_events events); - void (*cancel_io)(struct pa_mainloop_api*a, void* id); - - /* Fixed sources */ - void* (*source_fixed)(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, void *id, void *userdata), void *userdata); - void (*enable_fixed)(struct pa_mainloop_api*a, void* id, int b); - void (*cancel_fixed)(struct pa_mainloop_api*a, void* id); - - /* Idle sources */ - void* (*source_idle)(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, void *id, void *userdata), void *userdata); - void (*enable_idle)(struct pa_mainloop_api*a, void* id, int b); - void (*cancel_idle)(struct pa_mainloop_api*a, void* id); - - /* Time sources */ - void* (*source_time)(struct pa_mainloop_api*a, const struct timeval *tv, void (*callback) (struct pa_mainloop_api*a, void *id, const struct timeval *tv, void *userdata), void *userdata); - void (*enable_time)(struct pa_mainloop_api*a, void *id, const struct timeval *tv); - void (*cancel_time)(struct pa_mainloop_api*a, void* id); - - /* Exit mainloop */ - void (*quit)(struct pa_mainloop_api*a, int retval); -}; - -void pa_mainloop_api_once(struct pa_mainloop_api*m, void (*callback)(void *userdata), void *userdata); - -#endif diff --git a/src/mainloop-signal.c b/src/mainloop-signal.c deleted file mode 100644 index 642ca5e0..00000000 --- a/src/mainloop-signal.c +++ /dev/null @@ -1,163 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "mainloop-signal.h" -#include "util.h" - -struct signal_info { - int sig; - struct sigaction saved_sigaction; - void (*callback) (void *id, int signal, void *userdata); - void *userdata; - struct signal_info *previous, *next; -}; - -static struct pa_mainloop_api *api = NULL; -static int signal_pipe[2] = { -1, -1 }; -static void* mainloop_source = NULL; -static struct signal_info *signals = NULL; - -static void signal_handler(int sig) { - write(signal_pipe[1], &sig, sizeof(sig)); -} - -static void callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { - assert(a && id && events == PA_MAINLOOP_API_IO_EVENT_INPUT && id == mainloop_source && fd == signal_pipe[0]); - - for (;;) { - ssize_t r; - int sig; - struct signal_info*s; - - if ((r = read(signal_pipe[0], &sig, sizeof(sig))) < 0) { - if (errno == EAGAIN) - return; - - fprintf(stderr, "signal.c: read(): %s\n", strerror(errno)); - return; - } - - if (r != sizeof(sig)) { - fprintf(stderr, "signal.c: short read()\n"); - return; - } - - for (s = signals; s; s = s->next) - if (s->sig == sig) { - assert(s->callback); - s->callback(s, sig, s->userdata); - break; - } - } -} - -int pa_signal_init(struct pa_mainloop_api *a) { - assert(a); - if (pipe(signal_pipe) < 0) { - fprintf(stderr, "pipe() failed: %s\n", strerror(errno)); - return -1; - } - - pa_make_nonblock_fd(signal_pipe[0]); - pa_make_nonblock_fd(signal_pipe[1]); - - api = a; - mainloop_source = api->source_io(api, signal_pipe[0], PA_MAINLOOP_API_IO_EVENT_INPUT, callback, NULL); - assert(mainloop_source); - return 0; -} - -void pa_signal_done(void) { - assert(api && signal_pipe[0] >= 0 && signal_pipe[1] >= 0 && mainloop_source); - - api->cancel_io(api, mainloop_source); - mainloop_source = NULL; - - close(signal_pipe[0]); - close(signal_pipe[1]); - signal_pipe[0] = signal_pipe[1] = -1; - - while (signals) - pa_signal_unregister(signals); - - api = NULL; -} - -void* pa_signal_register(int sig, void (*callback) (void *id, int signal, void *userdata), void *userdata) { - struct signal_info *s = NULL; - struct sigaction sa; - assert(sig > 0 && callback); - - for (s = signals; s; s = s->next) - if (s->sig == sig) - goto fail; - - s = malloc(sizeof(struct signal_info)); - assert(s); - s->sig = sig; - s->callback = callback; - s->userdata = userdata; - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = signal_handler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - - if (sigaction(sig, &sa, &s->saved_sigaction) < 0) - goto fail; - - s->previous = NULL; - s->next = signals; - signals = s; - - return s; -fail: - if (s) - free(s); - return NULL; -} - -void pa_signal_unregister(void *id) { - struct signal_info *s = id; - assert(s); - - if (s->next) - s->next->previous = s->previous; - if (s->previous) - s->previous->next = s->next; - else - signals = s->next; - - sigaction(s->sig, &s->saved_sigaction, NULL); - free(s); -} diff --git a/src/mainloop-signal.h b/src/mainloop-signal.h deleted file mode 100644 index 8afe9c8d..00000000 --- a/src/mainloop-signal.h +++ /dev/null @@ -1,33 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "mainloop-api.h" - -int pa_signal_init(struct pa_mainloop_api *api); -void pa_signal_done(void); - -void* pa_signal_register(int signal, void (*callback) (void *id, int signal, void *userdata), void *userdata); -void pa_signal_unregister(void *id); - -#endif diff --git a/src/mainloop.c b/src/mainloop.c deleted file mode 100644 index b9eee86d..00000000 --- a/src/mainloop.c +++ /dev/null @@ -1,553 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mainloop.h" -#include "util.h" -#include "idxset.h" - -struct mainloop_source_header { - struct pa_mainloop *mainloop; - int dead; -}; - -struct mainloop_source_io { - struct mainloop_source_header header; - - int fd; - enum pa_mainloop_api_io_events events; - void (*callback) (struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata); - void *userdata; - - struct pollfd *pollfd; -}; - -struct mainloop_source_fixed_or_idle { - struct mainloop_source_header header; - int enabled; - - void (*callback)(struct pa_mainloop_api*a, void *id, void *userdata); - void *userdata; -}; - -struct mainloop_source_time { - struct mainloop_source_header header; - int enabled; - - struct timeval timeval; - void (*callback)(struct pa_mainloop_api*a, void *id, const struct timeval*tv, void *userdata); - void *userdata; -}; - -struct pa_mainloop { - struct pa_idxset *io_sources, *fixed_sources, *idle_sources, *time_sources; - int io_sources_scan_dead, fixed_sources_scan_dead, idle_sources_scan_dead, time_sources_scan_dead; - - struct pollfd *pollfds; - unsigned max_pollfds, n_pollfds; - int rebuild_pollfds; - - int quit, running, retval; - struct pa_mainloop_api api; -}; - -static void setup_api(struct pa_mainloop *m); - -struct pa_mainloop *pa_mainloop_new(void) { - struct pa_mainloop *m; - - m = malloc(sizeof(struct pa_mainloop)); - assert(m); - - m->io_sources = pa_idxset_new(NULL, NULL); - m->fixed_sources = pa_idxset_new(NULL, NULL); - m->idle_sources = pa_idxset_new(NULL, NULL); - m->time_sources = pa_idxset_new(NULL, NULL); - - assert(m->io_sources && m->fixed_sources && m->idle_sources && m->time_sources); - - m->io_sources_scan_dead = m->fixed_sources_scan_dead = m->idle_sources_scan_dead = m->time_sources_scan_dead = 0; - - m->pollfds = NULL; - m->max_pollfds = m->n_pollfds = m->rebuild_pollfds = 0; - - m->quit = m->running = m->retval = 0; - - setup_api(m); - - return m; -} - -static int foreach(void *p, uint32_t index, int *del, void*userdata) { - struct mainloop_source_header *h = p; - int *all = userdata; - assert(p && del && all); - - if (*all || h->dead) { - free(h); - *del = 1; - } - - return 0; -}; - -void pa_mainloop_free(struct pa_mainloop* m) { - int all = 1; - assert(m); - pa_idxset_foreach(m->io_sources, foreach, &all); - pa_idxset_foreach(m->fixed_sources, foreach, &all); - pa_idxset_foreach(m->idle_sources, foreach, &all); - pa_idxset_foreach(m->time_sources, foreach, &all); - - pa_idxset_free(m->io_sources, NULL, NULL); - pa_idxset_free(m->fixed_sources, NULL, NULL); - pa_idxset_free(m->idle_sources, NULL, NULL); - pa_idxset_free(m->time_sources, NULL, NULL); - - free(m->pollfds); - free(m); -} - -static void scan_dead(struct pa_mainloop *m) { - int all = 0; - assert(m); - if (m->io_sources_scan_dead) - pa_idxset_foreach(m->io_sources, foreach, &all); - if (m->fixed_sources_scan_dead) - pa_idxset_foreach(m->fixed_sources, foreach, &all); - if (m->idle_sources_scan_dead) - pa_idxset_foreach(m->idle_sources, foreach, &all); - if (m->time_sources_scan_dead) - pa_idxset_foreach(m->time_sources, foreach, &all); -} - -static void rebuild_pollfds(struct pa_mainloop *m) { - struct mainloop_source_io*s; - struct pollfd *p; - uint32_t index = PA_IDXSET_INVALID; - unsigned l; - - l = pa_idxset_ncontents(m->io_sources); - if (m->max_pollfds < l) { - m->pollfds = realloc(m->pollfds, sizeof(struct pollfd)*l); - m->max_pollfds = l; - } - - m->n_pollfds = 0; - p = m->pollfds; - for (s = pa_idxset_first(m->io_sources, &index); s; s = pa_idxset_next(m->io_sources, &index)) { - if (s->header.dead) { - s->pollfd = NULL; - continue; - } - - s->pollfd = p; - p->fd = s->fd; - p->events = ((s->events & PA_MAINLOOP_API_IO_EVENT_INPUT) ? POLLIN : 0) | ((s->events & PA_MAINLOOP_API_IO_EVENT_OUTPUT) ? POLLOUT : 0); - p->revents = 0; - - p++; - m->n_pollfds++; - } -} - -static void dispatch_pollfds(struct pa_mainloop *m) { - uint32_t index = PA_IDXSET_INVALID; - struct mainloop_source_io *s; - - for (s = pa_idxset_first(m->io_sources, &index); s; s = pa_idxset_next(m->io_sources, &index)) { - if (s->header.dead || !s->pollfd || !s->pollfd->revents) - continue; - - assert(s->pollfd->fd == s->fd && s->callback); - s->callback(&m->api, s, s->fd, - ((s->pollfd->revents & POLLHUP) ? PA_MAINLOOP_API_IO_EVENT_HUP : 0) | - ((s->pollfd->revents & POLLIN) ? PA_MAINLOOP_API_IO_EVENT_INPUT : 0) | - ((s->pollfd->revents & POLLOUT) ? PA_MAINLOOP_API_IO_EVENT_OUTPUT : 0), s->userdata); - s->pollfd->revents = 0; - } -} - -static void run_fixed_or_idle(struct pa_mainloop *m, struct pa_idxset *i) { - uint32_t index = PA_IDXSET_INVALID; - struct mainloop_source_fixed_or_idle *s; - - for (s = pa_idxset_first(i, &index); s; s = pa_idxset_next(i, &index)) { - if (s->header.dead || !s->enabled) - continue; - - assert(s->callback); - s->callback(&m->api, s, s->userdata); - } -} - -static int calc_next_timeout(struct pa_mainloop *m) { - uint32_t index = PA_IDXSET_INVALID; - struct mainloop_source_time *s; - struct timeval now; - int t = -1; - - if (pa_idxset_isempty(m->time_sources)) - return -1; - - gettimeofday(&now, NULL); - - for (s = pa_idxset_first(m->time_sources, &index); s; s = pa_idxset_next(m->time_sources, &index)) { - int tmp; - - if (s->header.dead || !s->enabled) - continue; - - if (s->timeval.tv_sec < now.tv_sec || (s->timeval.tv_sec == now.tv_sec && s->timeval.tv_usec <= now.tv_usec)) - return 0; - - tmp = (s->timeval.tv_sec - now.tv_sec)*1000; - - if (s->timeval.tv_usec > now.tv_usec) - tmp += (s->timeval.tv_usec - now.tv_usec)/1000; - else - tmp -= (now.tv_usec - s->timeval.tv_usec)/1000; - - if (tmp == 0) - return 0; - else if (t == -1 || tmp < t) - t = tmp; - } - - return t; -} - -static void dispatch_timeout(struct pa_mainloop *m) { - uint32_t index = PA_IDXSET_INVALID; - struct mainloop_source_time *s; - struct timeval now; - assert(m); - - if (pa_idxset_isempty(m->time_sources)) - return; - - gettimeofday(&now, NULL); - for (s = pa_idxset_first(m->time_sources, &index); s; s = pa_idxset_next(m->time_sources, &index)) { - - if (s->header.dead || !s->enabled) - continue; - - if (s->timeval.tv_sec < now.tv_sec || (s->timeval.tv_sec == now.tv_sec && s->timeval.tv_usec <= now.tv_usec)) { - assert(s->callback); - - s->enabled = 0; - s->callback(&m->api, s, &s->timeval, s->userdata); - } - } -} - -static int any_idle_sources(struct pa_mainloop *m) { - struct mainloop_source_fixed_or_idle *s; - uint32_t index; - assert(m); - - for (s = pa_idxset_first(m->idle_sources, &index); s; s = pa_idxset_next(m->idle_sources, &index)) - if (!s->header.dead && s->enabled) - return 1; - - return 0; -} - -int pa_mainloop_iterate(struct pa_mainloop *m, int block, int *retval) { - int r, idle; - assert(m && !m->running); - - if(m->quit) { - if (retval) - *retval = m->retval; - return 1; - } - - m->running = 1; - - scan_dead(m); - run_fixed_or_idle(m, m->fixed_sources); - - if (m->rebuild_pollfds) { - rebuild_pollfds(m); - m->rebuild_pollfds = 0; - } - - idle = any_idle_sources(m); - - do { - int t; - - if (!block || idle) - t = 0; - else - t = calc_next_timeout(m); - - r = poll(m->pollfds, m->n_pollfds, t); - } while (r < 0 && errno == EINTR); - - dispatch_timeout(m); - - if (r > 0) - dispatch_pollfds(m); - else if (r == 0 && idle) - run_fixed_or_idle(m, m->idle_sources); - else if (r < 0) - fprintf(stderr, "select(): %s\n", strerror(errno)); - - m->running = 0; - return r < 0 ? -1 : 0; -} - -int pa_mainloop_run(struct pa_mainloop *m, int *retval) { - int r; - while ((r = pa_mainloop_iterate(m, 1, retval)) == 0); - return r; -} - -void pa_mainloop_quit(struct pa_mainloop *m, int r) { - assert(m); - m->quit = r; -} - -/* IO sources */ -static void* mainloop_source_io(struct pa_mainloop_api*a, int fd, enum pa_mainloop_api_io_events events, void (*callback) (struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata), void *userdata) { - struct pa_mainloop *m; - struct mainloop_source_io *s; - assert(a && a->userdata && fd >= 0 && callback); - m = a->userdata; - assert(a == &m->api); - - s = malloc(sizeof(struct mainloop_source_io)); - assert(s); - s->header.mainloop = m; - s->header.dead = 0; - - s->fd = fd; - s->events = events; - s->callback = callback; - s->userdata = userdata; - s->pollfd = NULL; - - pa_idxset_put(m->io_sources, s, NULL); - m->rebuild_pollfds = 1; - return s; -} - -static void mainloop_enable_io(struct pa_mainloop_api*a, void* id, enum pa_mainloop_api_io_events events) { - struct pa_mainloop *m; - struct mainloop_source_io *s = id; - assert(a && a->userdata && s && !s->header.dead); - m = a->userdata; - assert(a == &m->api && s->header.mainloop == m); - - s->events = events; - if (s->pollfd) - s->pollfd->events = ((s->events & PA_MAINLOOP_API_IO_EVENT_INPUT) ? POLLIN : 0) | ((s->events & PA_MAINLOOP_API_IO_EVENT_OUTPUT) ? POLLOUT : 0); -} - -static void mainloop_cancel_io(struct pa_mainloop_api*a, void* id) { - struct pa_mainloop *m; - struct mainloop_source_io *s = id; - assert(a && a->userdata && s && !s->header.dead); - m = a->userdata; - assert(a == &m->api && s->header.mainloop == m); - - s->header.dead = 1; - m->io_sources_scan_dead = 1; - m->rebuild_pollfds = 1; -} - -/* Fixed sources */ -static void* mainloop_source_fixed(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, void *id, void *userdata), void *userdata) { - struct pa_mainloop *m; - struct mainloop_source_fixed_or_idle *s; - assert(a && a->userdata && callback); - m = a->userdata; - assert(a == &m->api); - - s = malloc(sizeof(struct mainloop_source_fixed_or_idle)); - assert(s); - s->header.mainloop = m; - s->header.dead = 0; - - s->enabled = 1; - s->callback = callback; - s->userdata = userdata; - - pa_idxset_put(m->fixed_sources, s, NULL); - return s; -} - -static void mainloop_enable_fixed(struct pa_mainloop_api*a, void* id, int b) { - struct pa_mainloop *m; - struct mainloop_source_fixed_or_idle *s = id; - assert(a && a->userdata && s && !s->header.dead); - m = a->userdata; - assert(a == &m->api); - - s->enabled = b; -} - -static void mainloop_cancel_fixed(struct pa_mainloop_api*a, void* id) { - struct pa_mainloop *m; - struct mainloop_source_fixed_or_idle *s = id; - assert(a && a->userdata && s && !s->header.dead); - m = a->userdata; - assert(a == &m->api); - - s->header.dead = 1; - m->fixed_sources_scan_dead = 1; -} - -/* Idle sources */ -static void* mainloop_source_idle(struct pa_mainloop_api*a, void (*callback) (struct pa_mainloop_api*a, void *id, void *userdata), void *userdata) { - struct pa_mainloop *m; - struct mainloop_source_fixed_or_idle *s; - assert(a && a->userdata && callback); - m = a->userdata; - assert(a == &m->api); - - s = malloc(sizeof(struct mainloop_source_fixed_or_idle)); - assert(s); - s->header.mainloop = m; - s->header.dead = 0; - - s->enabled = 1; - s->callback = callback; - s->userdata = userdata; - - pa_idxset_put(m->idle_sources, s, NULL); - return s; -} - -static void mainloop_cancel_idle(struct pa_mainloop_api*a, void* id) { - struct pa_mainloop *m; - struct mainloop_source_fixed_or_idle *s = id; - assert(a && a->userdata && s && !s->header.dead); - m = a->userdata; - assert(a == &m->api); - - s->header.dead = 1; - m->idle_sources_scan_dead = 1; -} - -/* Time sources */ -static void* mainloop_source_time(struct pa_mainloop_api*a, const struct timeval *tv, void (*callback) (struct pa_mainloop_api*a, void *id, const struct timeval *tv, void *userdata), void *userdata) { - struct pa_mainloop *m; - struct mainloop_source_time *s; - assert(a && a->userdata && callback); - m = a->userdata; - assert(a == &m->api); - - s = malloc(sizeof(struct mainloop_source_time)); - assert(s); - s->header.mainloop = m; - s->header.dead = 0; - - s->enabled = !!tv; - if (tv) - s->timeval = *tv; - - s->callback = callback; - s->userdata = userdata; - - pa_idxset_put(m->time_sources, s, NULL); - return s; -} - -static void mainloop_enable_time(struct pa_mainloop_api*a, void *id, const struct timeval *tv) { - struct pa_mainloop *m; - struct mainloop_source_time *s = id; - assert(a && a->userdata && s && !s->header.dead); - m = a->userdata; - assert(a == &m->api); - - if (tv) { - s->enabled = 1; - s->timeval = *tv; - } else - s->enabled = 0; -} - -static void mainloop_cancel_time(struct pa_mainloop_api*a, void* id) { - struct pa_mainloop *m; - struct mainloop_source_time *s = id; - assert(a && a->userdata && s && !s->header.dead); - m = a->userdata; - assert(a == &m->api); - - s->header.dead = 1; - m->time_sources_scan_dead = 1; - -} - -static void mainloop_quit(struct pa_mainloop_api*a, int retval) { - struct pa_mainloop *m; - assert(a && a->userdata); - m = a->userdata; - assert(a == &m->api); - - m->quit = 1; - m->retval = retval; -} - -static void setup_api(struct pa_mainloop *m) { - assert(m); - - m->api.userdata = m; - m->api.source_io = mainloop_source_io; - m->api.enable_io = mainloop_enable_io; - m->api.cancel_io = mainloop_cancel_io; - - m->api.source_fixed = mainloop_source_fixed; - m->api.enable_fixed = mainloop_enable_fixed; - m->api.cancel_fixed = mainloop_cancel_fixed; - - m->api.source_idle = mainloop_source_idle; - m->api.enable_idle = mainloop_enable_fixed; /* (!) */ - m->api.cancel_idle = mainloop_cancel_idle; - - m->api.source_time = mainloop_source_time; - m->api.enable_time = mainloop_enable_time; - m->api.cancel_time = mainloop_cancel_time; - - m->api.quit = mainloop_quit; -} - -struct pa_mainloop_api* pa_mainloop_get_api(struct pa_mainloop*m) { - assert(m); - return &m->api; -} - diff --git a/src/mainloop.h b/src/mainloop.h deleted file mode 100644 index 58448c3e..00000000 --- a/src/mainloop.h +++ /dev/null @@ -1,37 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "mainloop-api.h" - -struct pa_mainloop; - -struct pa_mainloop *pa_mainloop_new(void); -void pa_mainloop_free(struct pa_mainloop* m); - -int pa_mainloop_iterate(struct pa_mainloop *m, int block, int *retval); -int pa_mainloop_run(struct pa_mainloop *m, int *retval); - -struct pa_mainloop_api* pa_mainloop_get_api(struct pa_mainloop*m); - -#endif diff --git a/src/memblock.c b/src/memblock.c deleted file mode 100644 index 8f24ff22..00000000 --- a/src/memblock.c +++ /dev/null @@ -1,113 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include "memblock.h" - -static unsigned memblock_count = 0, memblock_total = 0; - -struct pa_memblock *pa_memblock_new(size_t length) { - struct pa_memblock *b = malloc(sizeof(struct pa_memblock)+length); - b->type = PA_MEMBLOCK_APPENDED; - b->ref = 1; - b->length = length; - b->data = b+1; - memblock_count++; - memblock_total += length; - return b; -} - -struct pa_memblock *pa_memblock_new_fixed(void *d, size_t length) { - struct pa_memblock *b = malloc(sizeof(struct pa_memblock)); - b->type = PA_MEMBLOCK_FIXED; - b->ref = 1; - b->length = length; - b->data = d; - memblock_count++; - memblock_total += length; - return b; -} - -struct pa_memblock *pa_memblock_new_dynamic(void *d, size_t length) { - struct pa_memblock *b = malloc(sizeof(struct pa_memblock)); - b->type = PA_MEMBLOCK_DYNAMIC; - b->ref = 1; - b->length = length; - b->data = d; - memblock_count++; - memblock_total += length; - return b; -} - -struct pa_memblock* pa_memblock_ref(struct pa_memblock*b) { - assert(b && b->ref >= 1); - b->ref++; - return b; -} - -void pa_memblock_unref(struct pa_memblock*b) { - assert(b && b->ref >= 1); - b->ref--; - - if (b->ref == 0) { - if (b->type == PA_MEMBLOCK_DYNAMIC) - free(b->data); - - memblock_count--; - memblock_total -= b->length; - - free(b); - } -} - -void pa_memblock_unref_fixed(struct pa_memblock *b) { - void *d; - - assert(b && b->ref >= 1); - - if (b->ref == 1) { - pa_memblock_unref(b); - return; - } else { - d = malloc(b->length); - assert(d); - memcpy(d, b->data, b->length); - b->data = d; - b->type = PA_MEMBLOCK_DYNAMIC; - b->ref--; - } -} - -unsigned pa_memblock_get_count(void) { - return memblock_count; -} - -unsigned pa_memblock_get_total(void) { - return memblock_total; -} diff --git a/src/memblock.h b/src/memblock.h deleted file mode 100644 index 4bb02977..00000000 --- a/src/memblock.h +++ /dev/null @@ -1,49 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include - -enum pa_memblock_type { PA_MEMBLOCK_FIXED, PA_MEMBLOCK_APPENDED, PA_MEMBLOCK_DYNAMIC }; - -struct pa_memblock { - enum pa_memblock_type type; - unsigned ref; - size_t length; - void *data; -}; - -struct pa_memblock *pa_memblock_new(size_t length); -struct pa_memblock *pa_memblock_new_fixed(void *data, size_t length); -struct pa_memblock *pa_memblock_new_dynamic(void *data, size_t length); - -void pa_memblock_unref(struct pa_memblock*b); -struct pa_memblock* pa_memblock_ref(struct pa_memblock*b); - -void pa_memblock_unref_fixed(struct pa_memblock*b); - -unsigned pa_memblock_get_count(void); -unsigned pa_memblock_get_total(void); - -#endif diff --git a/src/memblockq.c b/src/memblockq.c deleted file mode 100644 index eff923b9..00000000 --- a/src/memblockq.c +++ /dev/null @@ -1,326 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include - -#include "memblockq.h" - -struct memblock_list { - struct memblock_list *next; - struct pa_memchunk chunk; - struct timeval stamp; -}; - -struct pa_memblockq { - struct memblock_list *blocks, *blocks_tail; - unsigned n_blocks; - size_t current_length, maxlength, tlength, base, prebuf, minreq; - int measure_delay; - uint32_t delay; - struct pa_mcalign *mcalign; -}; - -struct pa_memblockq* pa_memblockq_new(size_t maxlength, size_t tlength, size_t base, size_t prebuf, size_t minreq) { - struct pa_memblockq* bq; - assert(maxlength && base && maxlength); - - bq = malloc(sizeof(struct pa_memblockq)); - assert(bq); - bq->blocks = bq->blocks_tail = 0; - bq->n_blocks = 0; - - bq->current_length = 0; - - fprintf(stderr, "memblockq requested: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", maxlength, tlength, base, prebuf, minreq); - - bq->base = base; - - bq->maxlength = ((maxlength+base-1)/base)*base; - assert(bq->maxlength >= base); - - bq->tlength = ((tlength+base-1)/base)*base; - if (bq->tlength == 0 || bq->tlength >= bq->maxlength) - bq->tlength = bq->maxlength; - - bq->prebuf = (prebuf == (size_t) -1) ? bq->maxlength/2 : prebuf; - bq->prebuf = (bq->prebuf/base)*base; - if (bq->prebuf > bq->maxlength) - bq->prebuf = bq->maxlength; - - bq->minreq = (minreq/base)*base; - if (bq->minreq == 0) - bq->minreq = 1; - - fprintf(stderr, "memblockq sanitized: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", bq->maxlength, bq->tlength, bq->base, bq->prebuf, bq->minreq); - - bq->measure_delay = 0; - bq->delay = 0; - - bq->mcalign = NULL; - - return bq; -} - -void pa_memblockq_free(struct pa_memblockq* bq) { - struct memblock_list *l; - assert(bq); - - if (bq->mcalign) - pa_mcalign_free(bq->mcalign); - - while ((l = bq->blocks)) { - bq->blocks = l->next; - pa_memblock_unref(l->chunk.memblock); - free(l); - } - - free(bq); -} - -void pa_memblockq_push(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta) { - struct memblock_list *q; - assert(bq && chunk && chunk->memblock && chunk->length && (chunk->length % bq->base) == 0); - - if (bq->blocks_tail && bq->blocks_tail->chunk.memblock == chunk->memblock) { - /* Try to merge memory chunks */ - - if (bq->blocks_tail->chunk.index+bq->blocks_tail->chunk.length == chunk->index) { - bq->blocks_tail->chunk.length += chunk->length; - bq->current_length += chunk->length; - - /* fprintf(stderr, __FILE__": merge succeeded: %u\n", chunk->length);*/ - return; - } - } - - q = malloc(sizeof(struct memblock_list)); - assert(q); - - if (bq->measure_delay) - gettimeofday(&q->stamp, NULL); - else - timerclear(&q->stamp); - - q->chunk = *chunk; - pa_memblock_ref(q->chunk.memblock); - assert(q->chunk.index+q->chunk.length <= q->chunk.memblock->length); - q->next = NULL; - - if (bq->blocks_tail) - bq->blocks_tail->next = q; - else - bq->blocks = q; - - bq->blocks_tail = q; - - bq->n_blocks++; - bq->current_length += chunk->length; - - pa_memblockq_shorten(bq, bq->maxlength); -} - -int pa_memblockq_peek(struct pa_memblockq* bq, struct pa_memchunk *chunk) { - assert(bq && chunk); - - if (!bq->blocks || bq->current_length < bq->prebuf) - return -1; - - bq->prebuf = 0; - - *chunk = bq->blocks->chunk; - pa_memblock_ref(chunk->memblock); - -/* if (chunk->memblock->ref != 2) */ -/* fprintf(stderr, "block %p with ref %u peeked.\n", chunk->memblock, chunk->memblock->ref); */ - - return 0; -} - -/* -int memblockq_pop(struct memblockq* bq, struct pa_memchunk *chunk) { - struct memblock_list *q; - - assert(bq && chunk); - - if (!bq->blocks || bq->current_length < bq->prebuf) - return -1; - - bq->prebuf = 0; - - q = bq->blocks; - bq->blocks = bq->blocks->next; - - *chunk = q->chunk; - - bq->n_blocks--; - bq->current_length -= chunk->length; - - free(q); - return 0; -} -*/ - -static uint32_t age(struct timeval *tv) { - assert(tv); - struct timeval now; - uint32_t r; - - if (tv->tv_sec == 0) - return 0; - - gettimeofday(&now, NULL); - - r = (now.tv_sec-tv->tv_sec) * 1000000; - - if (now.tv_usec >= tv->tv_usec) - r += now.tv_usec - tv->tv_usec; - else - r -= tv->tv_usec - now.tv_usec; - - return r; -} - -void pa_memblockq_drop(struct pa_memblockq *bq, size_t length) { - assert(bq && length && (length % bq->base) == 0); - - while (length > 0) { - size_t l = length; - assert(bq->blocks && bq->current_length >= length); - - if (l > bq->blocks->chunk.length) - l = bq->blocks->chunk.length; - - if (bq->measure_delay) - bq->delay = age(&bq->blocks->stamp); - - bq->blocks->chunk.index += l; - bq->blocks->chunk.length -= l; - bq->current_length -= l; - - if (bq->blocks->chunk.length == 0) { - struct memblock_list *q; - - q = bq->blocks; - bq->blocks = bq->blocks->next; - if (bq->blocks == NULL) - bq->blocks_tail = NULL; - pa_memblock_unref(q->chunk.memblock); - free(q); - - bq->n_blocks--; - } - - length -= l; - } -} - -void pa_memblockq_shorten(struct pa_memblockq *bq, size_t length) { - size_t l; - assert(bq); - - if (bq->current_length <= length) - return; - - fprintf(stderr, "Warning! pa_memblockq_shorten()\n"); - - l = bq->current_length - length; - l /= bq->base; - l *= bq->base; - - pa_memblockq_drop(bq, l); -} - - -void pa_memblockq_empty(struct pa_memblockq *bq) { - assert(bq); - pa_memblockq_shorten(bq, 0); -} - -int pa_memblockq_is_readable(struct pa_memblockq *bq) { - assert(bq); - - return bq->current_length && (bq->current_length >= bq->prebuf); -} - -int pa_memblockq_is_writable(struct pa_memblockq *bq, size_t length) { - assert(bq); - - return bq->current_length + length <= bq->tlength; -} - -uint32_t pa_memblockq_get_delay(struct pa_memblockq *bq) { - assert(bq); - return bq->delay; -} - -uint32_t pa_memblockq_get_length(struct pa_memblockq *bq) { - assert(bq); - return bq->current_length; -} - -uint32_t pa_memblockq_missing(struct pa_memblockq *bq) { - size_t l; - assert(bq); - - if (bq->current_length >= bq->tlength) - return 0; - - l = bq->tlength - bq->current_length; - assert(l); - - return (l >= bq->minreq) ? l : 0; -} - -void pa_memblockq_push_align(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta) { - struct pa_memchunk rchunk; - assert(bq && chunk && bq->base); - - if (bq->base == 1) { - pa_memblockq_push(bq, chunk, delta); - return; - } - - if (!bq->mcalign) { - bq->mcalign = pa_mcalign_new(bq->base); - assert(bq->mcalign); - } - - pa_mcalign_push(bq->mcalign, chunk); - - while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) { - pa_memblockq_push(bq, &rchunk, delta); - pa_memblock_unref(rchunk.memblock); - delta = 0; - } -} - -uint32_t pa_memblockq_get_minreq(struct pa_memblockq *bq) { - assert(bq); - return bq->minreq; -} diff --git a/src/memblockq.h b/src/memblockq.h deleted file mode 100644 index e6ad01db..00000000 --- a/src/memblockq.h +++ /dev/null @@ -1,82 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -#include "memblock.h" -#include "memchunk.h" - -struct pa_memblockq; - -/* Parameters: - - maxlength: maximum length of queue. If more data is pushed into the queue, data from the front is dropped - - length: the target length of the queue. - - base: a base value for all metrics. Only multiples of this value are popped from the queue - - prebuf: before passing the first byte out, make sure that enough bytes are in the queue - - minreq: pa_memblockq_missing() will only return values greater than this value -*/ -struct pa_memblockq* pa_memblockq_new(size_t maxlength, - size_t tlength, - size_t base, - size_t prebuf, - size_t minreq); -void pa_memblockq_free(struct pa_memblockq*bq); - -/* Push a new memory chunk into the queue. Optionally specify a value for future cancellation. This is currently not implemented, however! */ -void pa_memblockq_push(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta); - -/* Same as pa_memblockq_push(), however chunks are filtered through a mcalign object, and thus aligned to multiples of base */ -void pa_memblockq_push_align(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta); - -/* Return a copy of the next memory chunk in the queue. It is not removed from the queue */ -int pa_memblockq_peek(struct pa_memblockq* bq, struct pa_memchunk *chunk); - -/* Drop the specified bytes from the queue */ -void pa_memblockq_drop(struct pa_memblockq *bq, size_t length); - -/* Shorten the pa_memblockq to the specified length by dropping data at the end of the queue */ -void pa_memblockq_shorten(struct pa_memblockq *bq, size_t length); - -/* Empty the pa_memblockq */ -void pa_memblockq_empty(struct pa_memblockq *bq); - -/* Test if the pa_memblockq is currently readable, that is, more data than base */ -int pa_memblockq_is_readable(struct pa_memblockq *bq); - -/* Test if the pa_memblockq is currently writable for the specified amount of bytes */ -int pa_memblockq_is_writable(struct pa_memblockq *bq, size_t length); - -/* The time memory chunks stay in the queue until they are removed completely in usecs */ -uint32_t pa_memblockq_get_delay(struct pa_memblockq *bq); - -/* Return the length of the queue in bytes */ -uint32_t pa_memblockq_get_length(struct pa_memblockq *bq); - -/* Return how many bytes are missing in queue to the specified fill amount */ -uint32_t pa_memblockq_missing(struct pa_memblockq *bq); - - -uint32_t pa_memblockq_get_minreq(struct pa_memblockq *bq); - -#endif diff --git a/src/memchunk.c b/src/memchunk.c deleted file mode 100644 index d27ca61a..00000000 --- a/src/memchunk.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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include "memchunk.h" - -void pa_memchunk_make_writable(struct pa_memchunk *c) { - struct pa_memblock *n; - assert(c && c->memblock && c->memblock->ref >= 1); - - if (c->memblock->ref == 1) - return; - - n = pa_memblock_new(c->length); - assert(n); - memcpy(n->data, c->memblock->data+c->index, c->length); - pa_memblock_unref(c->memblock); - c->memblock = n; - c->index = 0; -} - - -struct pa_mcalign { - size_t base; - struct pa_memchunk chunk; - uint8_t *buffer; - size_t buffer_fill; -}; - -struct pa_mcalign *pa_mcalign_new(size_t base) { - struct pa_mcalign *m; - assert(base); - - m = malloc(sizeof(struct pa_mcalign)); - assert(m); - m->base = base; - m->chunk.memblock = NULL; - m->chunk.length = m->chunk.index = 0; - m->buffer = NULL; - m->buffer_fill = 0; - return m; -} - -void pa_mcalign_free(struct pa_mcalign *m) { - assert(m); - - free(m->buffer); - - if (m->chunk.memblock) - pa_memblock_unref(m->chunk.memblock); - - free(m); -} - -void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c) { - assert(m && c && !m->chunk.memblock && c->memblock && c->length); - - m->chunk = *c; - pa_memblock_ref(m->chunk.memblock); -} - -int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c) { - assert(m && c && m->base > m->buffer_fill); - int ret; - - if (!m->chunk.memblock) - return -1; - - if (m->buffer_fill) { - size_t l = m->base - m->buffer_fill; - if (l > m->chunk.length) - l = m->chunk.length; - assert(m->buffer && l); - - memcpy(m->buffer + m->buffer_fill, m->chunk.memblock->data + m->chunk.index, l); - m->buffer_fill += l; - m->chunk.index += l; - m->chunk.length -= l; - - if (m->chunk.length == 0) { - m->chunk.length = m->chunk.index = 0; - pa_memblock_unref(m->chunk.memblock); - m->chunk.memblock = NULL; - } - - assert(m->buffer_fill <= m->base); - if (m->buffer_fill == m->base) { - c->memblock = pa_memblock_new_dynamic(m->buffer, m->base); - assert(c->memblock); - c->index = 0; - c->length = m->base; - m->buffer = NULL; - m->buffer_fill = 0; - - return 0; - } - - return -1; - } - - m->buffer_fill = m->chunk.length % m->base; - - if (m->buffer_fill) { - assert(!m->buffer); - m->buffer = malloc(m->base); - assert(m->buffer); - m->chunk.length -= m->buffer_fill; - memcpy(m->buffer, m->chunk.memblock->data + m->chunk.index + m->chunk.length, m->buffer_fill); - } - - if (m->chunk.length) { - *c = m->chunk; - pa_memblock_ref(c->memblock); - ret = 0; - } else - ret = -1; - - m->chunk.length = m->chunk.index = 0; - pa_memblock_unref(m->chunk.memblock); - m->chunk.memblock = NULL; - - return ret; -} diff --git a/src/memchunk.h b/src/memchunk.h deleted file mode 100644 index 341c145c..00000000 --- a/src/memchunk.h +++ /dev/null @@ -1,41 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "memblock.h" - -struct pa_memchunk { - struct pa_memblock *memblock; - size_t index, length; -}; - -void pa_memchunk_make_writable(struct pa_memchunk *c); - -struct pa_mcalign; - -struct pa_mcalign *pa_mcalign_new(size_t base); -void pa_mcalign_free(struct pa_mcalign *m); -void pa_mcalign_push(struct pa_mcalign *m, const struct pa_memchunk *c); -int pa_mcalign_pop(struct pa_mcalign *m, struct pa_memchunk *c); - -#endif diff --git a/src/modargs.c b/src/modargs.c deleted file mode 100644 index 280a546d..00000000 --- a/src/modargs.c +++ /dev/null @@ -1,288 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#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" - -struct pa_modargs; - -struct entry { - char *key, *value; -}; - -static int add_key_value(struct pa_hashmap *map, char *key, char *value, const char* const* valid_keys) { - struct entry *e; - assert(map && key && value); - - if (valid_keys) { - const char*const* v; - for (v = valid_keys; *v; v++) - if (strcmp(*v, key) == 0) - break; - - if (!*v) { - free(key); - free(value); - return -1; - } - } - - e = malloc(sizeof(struct entry)); - assert(e); - e->key = key; - e->value = value; - pa_hashmap_put(map, key, e); - return 0; -} - -struct pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) { - struct pa_hashmap *map = NULL; - - map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - assert(map); - - if (args) { - enum { WHITESPACE, KEY, VALUE_START, VALUE_SIMPLE, VALUE_DOUBLE_QUOTES, VALUE_TICKS } state; - const char *p, *key, *value; - size_t key_len, value_len; - - key = value = NULL; - state = WHITESPACE; - for (p = args; *p; p++) { - switch (state) { - case WHITESPACE: - if (*p == '=') - goto fail; - else if (!isspace(*p)) { - key = p; - state = KEY; - key_len = 1; - } - break; - case KEY: - if (*p == '=') - state = VALUE_START; - else - key_len++; - break; - case VALUE_START: - if (*p == '\'') { - state = VALUE_TICKS; - value = p+1; - value_len = 0; - } else if (*p == '"') { - state = VALUE_DOUBLE_QUOTES; - value = p+1; - value_len = 0; - } else if (isspace(*p)) { - if (add_key_value(map, strndup(key, key_len), strdup(""), valid_keys) < 0) - goto fail; - state = WHITESPACE; - } else { - state = VALUE_SIMPLE; - value = p; - value_len = 1; - } - break; - case VALUE_SIMPLE: - if (isspace(*p)) { - if (add_key_value(map, strndup(key, key_len), strndup(value, value_len), valid_keys) < 0) - goto fail; - state = WHITESPACE; - } else - value_len++; - break; - case VALUE_DOUBLE_QUOTES: - if (*p == '"') { - if (add_key_value(map, strndup(key, key_len), strndup(value, value_len), valid_keys) < 0) - goto fail; - state = WHITESPACE; - } else - value_len++; - break; - case VALUE_TICKS: - if (*p == '\'') { - if (add_key_value(map, strndup(key, key_len), strndup(value, value_len), valid_keys) < 0) - goto fail; - state = WHITESPACE; - } else - value_len++; - break; - } - } - - if (state == VALUE_START) { - if (add_key_value(map, strndup(key, key_len), strdup(""), valid_keys) < 0) - goto fail; - } else if (state == VALUE_SIMPLE) { - if (add_key_value(map, strndup(key, key_len), strdup(value), valid_keys) < 0) - goto fail; - } else if (state != WHITESPACE) - goto fail; - } - - return (struct pa_modargs*) map; - -fail: - - if (map) - pa_modargs_free((struct pa_modargs*) map); - - return NULL; -} - - -static void free_func(void *p, void*userdata) { - struct entry *e = p; - assert(e); - free(e->key); - free(e->value); - free(e); -} - -void pa_modargs_free(struct pa_modargs*ma) { - struct pa_hashmap *map = (struct pa_hashmap*) ma; - pa_hashmap_free(map, free_func, NULL); -} - -const char *pa_modargs_get_value(struct pa_modargs *ma, const char *key, const char *def) { - struct pa_hashmap *map = (struct pa_hashmap*) ma; - struct entry*e; - - if (!(e = pa_hashmap_get(map, key))) - return def; - - return e->value; -} - -int pa_modargs_get_value_u32(struct pa_modargs *ma, const char *key, uint32_t *value) { - const char *v; - char *e; - unsigned long l; - assert(ma && key && value); - - if (!(v = pa_modargs_get_value(ma, key, NULL))) - return 0; - - if (!*v) - return -1; - - l = strtoul(v, &e, 0); - if (*e) - return -1; - - *value = (uint32_t) l; - return 0; -} - -int pa_modargs_get_sample_spec(struct pa_modargs *ma, struct pa_sample_spec *rss) { - const char *format; - uint32_t channels; - struct pa_sample_spec ss; - assert(ma && rss); - - ss = *rss; - if ((pa_modargs_get_value_u32(ma, "rate", &ss.rate)) < 0) - return -1; - - channels = ss.channels; - if ((pa_modargs_get_value_u32(ma, "channels", &channels)) < 0) - return -1; - ss.channels = (uint8_t) channels; - - if ((format = pa_modargs_get_value(ma, "format", NULL))) { - if (strcmp(format, "s16le") == 0) - ss.format = PA_SAMPLE_S16LE; - else if (strcmp(format, "s16be") == 0) - ss.format = PA_SAMPLE_S16BE; - else if (strcmp(format, "s16ne") == 0 || strcmp(format, "s16") == 0 || strcmp(format, "16") == 0) - ss.format = PA_SAMPLE_S16NE; - else if (strcmp(format, "u8") == 0 || strcmp(format, "8") == 0) - ss.format = PA_SAMPLE_U8; - else if (strcmp(format, "float32") == 0 || strcmp(format, "float32ne") == 0) - ss.format = PA_SAMPLE_FLOAT32; - else if (strcmp(format, "float32le") == 0) - ss.format = PA_SAMPLE_FLOAT32LE; - else if (strcmp(format, "float32be") == 0) - ss.format = PA_SAMPLE_FLOAT32BE; - else if (strcmp(format, "ulaw") == 0) - ss.format = PA_SAMPLE_ULAW; - else if (strcmp(format, "alaw") == 0) - ss.format = PA_SAMPLE_ALAW; - else - return -1; - } - - if (!pa_sample_spec_valid(&ss)) - return -1; - - *rss = ss; - - return 0; -} - -int pa_modargs_get_source_index(struct pa_modargs *ma, struct pa_core *c, uint32_t *index) { - const char *t; - assert(ma && index); - - if (!(t = pa_modargs_get_value(ma, "source", NULL))) - *index = PA_IDXSET_INVALID; - else { - struct pa_source *source; - if (!(source = pa_namereg_get(c, t, PA_NAMEREG_SOURCE))) - return -1; - - *index = source->index; - } - - return 0; -} - -int pa_modargs_get_sink_index(struct pa_modargs *ma, struct pa_core *c, uint32_t *index) { - const char *t; - assert(ma && index); - - if (!(t = pa_modargs_get_value(ma, "sink", NULL))) - *index = PA_IDXSET_INVALID; - else { - struct pa_sink *sink; - if (!(sink = pa_namereg_get(c, t, PA_NAMEREG_SINK))) - return -1; - - *index = sink->index; - } - - return 0; -} diff --git a/src/modargs.h b/src/modargs.h deleted file mode 100644 index 301dc297..00000000 --- a/src/modargs.h +++ /dev/null @@ -1,42 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include "sample.h" -#include "core.h" - -struct pa_modargs; - -struct pa_modargs *pa_modargs_new(const char *args, const char* const* keys); -void pa_modargs_free(struct pa_modargs*ma); - -const char *pa_modargs_get_value(struct pa_modargs *ma, const char *key, const char *def); -int pa_modargs_get_value_u32(struct pa_modargs *ma, const char *key, uint32_t *value); - -int pa_modargs_get_sample_spec(struct pa_modargs *ma, struct pa_sample_spec *ss); - -int pa_modargs_get_source_index(struct pa_modargs *ma, struct pa_core *c, uint32_t *index); -int pa_modargs_get_sink_index(struct pa_modargs *ma, struct pa_core *c, uint32_t *index); - -#endif diff --git a/src/module-alsa-sink.c b/src/module-alsa-sink.c deleted file mode 100644 index 8a3388af..00000000 --- a/src/module-alsa-sink.c +++ /dev/null @@ -1,259 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#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" - -struct userdata { - snd_pcm_t *pcm_handle; - struct pa_sink *sink; - void **io_sources; - unsigned n_io_sources; - - size_t frame_size, fragment_size; - struct pa_memchunk memchunk, silence; -}; - -static const char* const valid_modargs[] = { - "device", - "sink_name", - "format", - "channels", - "rate", - "fragments", - "fragment_size", - NULL -}; - -#define DEFAULT_SINK_NAME "alsa_output" -#define DEFAULT_DEVICE "plughw:0,0" - -static void xrun_recovery(struct userdata *u) { - assert(u); - - fprintf(stderr, "*** ALSA-XRUN (playback) ***\n"); - - if (snd_pcm_prepare(u->pcm_handle) < 0) - fprintf(stderr, "snd_pcm_prepare() failed\n"); -} - -static void do_write(struct userdata *u) { - assert(u); - - for (;;) { - struct pa_memchunk *memchunk = NULL; - snd_pcm_sframes_t frames; - - if (u->memchunk.memblock) - memchunk = &u->memchunk; - else { - if (pa_sink_render(u->sink, u->fragment_size, &u->memchunk) < 0) - memchunk = &u->silence; - else - memchunk = &u->memchunk; - } - - assert(memchunk->memblock && memchunk->memblock->data && memchunk->length && memchunk->memblock->length && (memchunk->length % u->frame_size) == 0); - - if ((frames = snd_pcm_writei(u->pcm_handle, memchunk->memblock->data + memchunk->index, memchunk->length / u->frame_size)) < 0) { - if (frames == -EAGAIN) - return; - - if (frames == -EPIPE) { - xrun_recovery(u); - continue; - } - - fprintf(stderr, "snd_pcm_writei() failed\n"); - return; - } - - if (memchunk == &u->memchunk) { - size_t l = frames * u->frame_size; - memchunk->index += l; - memchunk->length -= l; - - if (memchunk->length == 0) { - pa_memblock_unref(memchunk->memblock); - memchunk->memblock = NULL; - memchunk->index = memchunk->length = 0; - } - } - - break; - } -} - -static void io_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { - struct userdata *u = userdata; - assert(u && a && id); - - if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN) - xrun_recovery(u); - - do_write(u); -} - -static uint32_t sink_get_latency_cb(struct pa_sink *s) { - struct userdata *u = s->userdata; - snd_pcm_sframes_t frames; - assert(s && u && u->sink); - - if (snd_pcm_delay(u->pcm_handle, &frames) < 0) { - fprintf(stderr, __FILE__": failed to get delay\n"); - s->get_latency = NULL; - return 0; - } - - if (frames < 0) - frames = 0; - - return pa_samples_usec(frames * u->frame_size, &s->sample_spec); -} - -int pa_module_init(struct pa_core *c, struct pa_module*m) { - struct pa_modargs *ma = NULL; - int ret = -1; - struct userdata *u = NULL; - const char *dev; - struct pa_sample_spec ss; - unsigned periods, fragsize; - snd_pcm_uframes_t buffer_size; - size_t frame_size; - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - fprintf(stderr, __FILE__": failed to parse module arguments\n"); - goto fail; - } - - ss = c->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &ss) < 0) { - fprintf(stderr, __FILE__": failed to parse sample specification\n"); - goto fail; - } - frame_size = pa_sample_size(&ss); - - periods = 12; - fragsize = 1024; - if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) { - fprintf(stderr, __FILE__": failed to parse buffer metrics\n"); - goto fail; - } - buffer_size = fragsize/frame_size*periods; - - u = malloc(sizeof(struct userdata)); - assert(u); - memset(u, 0, sizeof(struct userdata)); - m->userdata = u; - - if (snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) { - fprintf(stderr, __FILE__": Error opening PCM device %s\n", dev); - goto fail; - } - - if (pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &buffer_size) < 0) { - fprintf(stderr, __FILE__": Failed to set hardware parameters\n"); - goto fail; - } - - u->sink = pa_sink_new(c, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss); - assert(u->sink); - - u->sink->get_latency = sink_get_latency_cb; - u->sink->userdata = u; - pa_sink_set_owner(u->sink, m); - u->sink->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev); - - if (pa_create_io_sources(u->pcm_handle, c->mainloop, &u->io_sources, &u->n_io_sources, io_callback, u) < 0) { - fprintf(stderr, __FILE__": failed to obtain file descriptors\n"); - goto fail; - } - - u->frame_size = frame_size; - u->fragment_size = buffer_size*u->frame_size/periods; - - fprintf(stderr, __FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size); - - u->silence.memblock = pa_memblock_new(u->silence.length = u->fragment_size); - assert(u->silence.memblock); - pa_silence_memblock(u->silence.memblock, &ss); - u->silence.index = 0; - - u->memchunk.memblock = NULL; - u->memchunk.index = u->memchunk.length = 0; - - ret = 0; - -finish: - if (ma) - pa_modargs_free(ma); - - return ret; - -fail: - - if (u) - pa_module_done(c, m); - - goto finish; -} - -void pa_module_done(struct pa_core *c, struct pa_module*m) { - struct userdata *u; - assert(c && m); - - if ((u = m->userdata)) { - if (u->sink) - pa_sink_free(u->sink); - - if (u->io_sources) - pa_free_io_sources(c->mainloop, u->io_sources, u->n_io_sources); - - if (u->pcm_handle) { - snd_pcm_drop(u->pcm_handle); - snd_pcm_close(u->pcm_handle); - } - - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); - if (u->silence.memblock) - pa_memblock_unref(u->silence.memblock); - - free(u); - } -} - diff --git a/src/module-alsa-source.c b/src/module-alsa-source.c deleted file mode 100644 index 287a0350..00000000 --- a/src/module-alsa-source.c +++ /dev/null @@ -1,237 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#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" - -struct userdata { - snd_pcm_t *pcm_handle; - struct pa_source *source; - void **io_sources; - unsigned n_io_sources; - - size_t frame_size, fragment_size; - struct pa_memchunk memchunk; -}; - -static const char* const valid_modargs[] = { - "device", - "source_name", - "format", - "channels", - "rate", - "fragments", - "fragment_size", - NULL -}; - -#define DEFAULT_SOURCE_NAME "alsa_input" -#define DEFAULT_DEVICE "hw:0,0" - -static void xrun_recovery(struct userdata *u) { - assert(u); - - fprintf(stderr, "*** ALSA-XRUN (capture) ***\n"); - - if (snd_pcm_prepare(u->pcm_handle) < 0) - fprintf(stderr, "snd_pcm_prepare() failed\n"); -} - -static void do_read(struct userdata *u) { - assert(u); - - for (;;) { - struct pa_memchunk post_memchunk; - snd_pcm_sframes_t frames; - size_t l; - - if (!u->memchunk.memblock) { - u->memchunk.memblock = pa_memblock_new(u->memchunk.length = u->fragment_size); - u->memchunk.index = 0; - } - - assert(u->memchunk.memblock && u->memchunk.memblock->data && u->memchunk.length && u->memchunk.memblock->length && (u->memchunk.length % u->frame_size) == 0); - - if ((frames = snd_pcm_readi(u->pcm_handle, u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length / u->frame_size)) < 0) { - if (frames == -EAGAIN) - return; - - if (frames == -EPIPE) { - xrun_recovery(u); - continue; - } - - fprintf(stderr, "snd_pcm_readi() failed: %s\n", strerror(-frames)); - return; - } - - l = frames * u->frame_size; - - post_memchunk = u->memchunk; - post_memchunk.length = l; - - pa_source_post(u->source, &post_memchunk); - - u->memchunk.index += l; - u->memchunk.length -= l; - - if (u->memchunk.length == 0) { - pa_memblock_unref(u->memchunk.memblock); - u->memchunk.memblock = NULL; - u->memchunk.index = u->memchunk.length = 0; - } - - break; - } -} - -static void io_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { - struct userdata *u = userdata; - assert(u && a && id); - - if (snd_pcm_state(u->pcm_handle) == SND_PCM_STATE_XRUN) - xrun_recovery(u); - - do_read(u); -} - -int pa_module_init(struct pa_core *c, struct pa_module*m) { - struct pa_modargs *ma = NULL; - int ret = -1; - struct userdata *u = NULL; - const char *dev; - struct pa_sample_spec ss; - unsigned periods, fragsize; - snd_pcm_uframes_t buffer_size; - size_t frame_size; - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - fprintf(stderr, __FILE__": failed to parse module arguments\n"); - goto fail; - } - - ss = c->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &ss) < 0) { - fprintf(stderr, __FILE__": failed to parse sample specification\n"); - goto fail; - } - frame_size = pa_sample_size(&ss); - - periods = 12; - fragsize = 1024; - if (pa_modargs_get_value_u32(ma, "fragments", &periods) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &fragsize) < 0) { - fprintf(stderr, __FILE__": failed to parse buffer metrics\n"); - goto fail; - } - buffer_size = fragsize/frame_size*periods; - - u = malloc(sizeof(struct userdata)); - assert(u); - memset(u, 0, sizeof(struct userdata)); - m->userdata = u; - - if (snd_pcm_open(&u->pcm_handle, dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) < 0) { - fprintf(stderr, __FILE__": Error opening PCM device %s\n", dev); - goto fail; - } - - if (pa_alsa_set_hw_params(u->pcm_handle, &ss, &periods, &buffer_size) < 0) { - fprintf(stderr, __FILE__": Failed to set hardware parameters\n"); - goto fail; - } - - u->source = pa_source_new(c, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss); - assert(u->source); - - u->source->userdata = u; - pa_source_set_owner(u->source, m); - u->source->description = pa_sprintf_malloc("Advanced Linux Sound Architecture PCM on '%s'", dev); - - if (pa_create_io_sources(u->pcm_handle, c->mainloop, &u->io_sources, &u->n_io_sources, io_callback, u) < 0) { - fprintf(stderr, __FILE__": failed to obtain file descriptors\n"); - goto fail; - } - - u->frame_size = frame_size; - u->fragment_size = buffer_size*u->frame_size/periods; - - fprintf(stderr, __FILE__": using %u fragments of size %u bytes.\n", periods, u->fragment_size); - - u->memchunk.memblock = NULL; - u->memchunk.index = u->memchunk.length = 0; - - snd_pcm_start(u->pcm_handle); - - ret = 0; - -finish: - if (ma) - pa_modargs_free(ma); - - return ret; - -fail: - - if (u) - pa_module_done(c, m); - - goto finish; -} - -void pa_module_done(struct pa_core *c, struct pa_module*m) { - struct userdata *u; - assert(c && m); - - if ((u = m->userdata)) { - if (u->source) - pa_source_free(u->source); - - if (u->io_sources) - pa_free_io_sources(c->mainloop, u->io_sources, u->n_io_sources); - - if (u->pcm_handle) { - snd_pcm_drop(u->pcm_handle); - snd_pcm_close(u->pcm_handle); - } - - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); - - free(u); - } -} - diff --git a/src/module-cli.c b/src/module-cli.c deleted file mode 100644 index 8897c9c6..00000000 --- a/src/module-cli.c +++ /dev/null @@ -1,73 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include "module.h" -#include "iochannel.h" -#include "cli.h" -#include "sioman.h" - -static void eof_cb(struct pa_cli*c, void *userdata) { - struct pa_module *m = userdata; - assert(c && m); - - pa_module_unload_request(m->core, m); -} - -int pa_module_init(struct pa_core *c, struct pa_module*m) { - struct pa_iochannel *io; - assert(c && m); - - if (m->argument) { - fprintf(stderr, __FILE__": module doesn't accept arguments.\n"); - return -1; - } - - if (pa_stdio_acquire() < 0) { - fprintf(stderr, __FILE__": STDIN/STDUSE already in use.\n"); - return -1; - } - - io = pa_iochannel_new(c->mainloop, STDIN_FILENO, STDOUT_FILENO); - assert(io); - pa_iochannel_set_noclose(io, 1); - - m->userdata = pa_cli_new(c, io, m); - assert(m->userdata); - - pa_cli_set_eof_callback(m->userdata, eof_cb, m); - - return 0; -} - -void pa_module_done(struct pa_core *c, struct pa_module*m) { - assert(c && m); - - pa_cli_free(m->userdata); - pa_stdio_release(); -} diff --git a/src/module-oss-mmap.c b/src/module-oss-mmap.c deleted file mode 100644 index 800eaf25..00000000 --- a/src/module-oss-mmap.c +++ /dev/null @@ -1,404 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#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" - -struct userdata { - struct pa_sink *sink; - struct pa_source *source; - struct pa_core *core; - struct pa_sample_spec sample_spec; - - size_t in_fragment_size, out_fragment_size, in_fragments, out_fragments, out_fill; - - int fd; - - void *in_mmap, *out_mmap; - size_t in_mmap_length, out_mmap_length; - - void *mainloop_source; - - struct pa_memblock **in_memblocks, **out_memblocks; - unsigned out_current, in_current; -}; - -static const char* const valid_modargs[] = { - "sink_name", - "source_name", - "device", - "record", - "playback", - "fragments", - "fragment_size", - "format", - "rate", - "channels", - NULL -}; - -#define DEFAULT_SINK_NAME "oss_output" -#define DEFAULT_SOURCE_NAME "oss_input" -#define DEFAULT_DEVICE "/dev/dsp" - -static void out_fill_memblocks(struct userdata *u, unsigned n) { - assert(u && u->out_memblocks); - - while (n > 0) { - struct pa_memchunk chunk; - - if (u->out_memblocks[u->out_current]) - pa_memblock_unref_fixed(u->out_memblocks[u->out_current]); - - chunk.memblock = u->out_memblocks[u->out_current] = pa_memblock_new_fixed(u->out_mmap+u->out_fragment_size*u->out_current, u->out_fragment_size); - assert(chunk.memblock); - chunk.length = chunk.memblock->length; - chunk.index = 0; - - pa_sink_render_into_full(u->sink, &chunk); - - u->out_current++; - while (u->out_current >= u->out_fragments) - u->out_current -= u->out_fragments; - - n--; - } -} - -static void do_write(struct userdata *u) { - struct count_info info; - assert(u && u->sink); - - if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) { - fprintf(stderr, "SNDCTL_DSP_GETOPTR: %s\n", strerror(errno)); - return; - } - - u->out_fill = (u->out_fragment_size * u->out_fragments) - (info.ptr % u->out_fragment_size); - - if (!info.blocks) - return; - - out_fill_memblocks(u, info.blocks); -} - -static void in_post_memblocks(struct userdata *u, unsigned n) { - assert(u && u->in_memblocks); - - while (n > 0) { - struct pa_memchunk chunk; - - if (!u->in_memblocks[u->in_current]) { - chunk.memblock = u->in_memblocks[u->in_current] = pa_memblock_new_fixed(u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size); - chunk.length = chunk.memblock->length; - chunk.index = 0; - - pa_source_post(u->source, &chunk); - } - - u->in_current++; - while (u->in_current >= u->in_fragments) - u->in_current -= u->in_fragments; - - n--; - } -} - -static void in_clear_memblocks(struct userdata*u, unsigned n) { - unsigned i = u->in_current; - assert(u && u->in_memblocks); - - if (n > u->in_fragments) - n = u->in_fragments; - - while (n > 0) { - if (u->in_memblocks[i]) { - pa_memblock_unref_fixed(u->in_memblocks[i]); - u->in_memblocks[i] = NULL; - } - - i++; - while (i >= u->in_fragments) - i -= u->in_fragments; - - n--; - } -} - -static void do_read(struct userdata *u) { - struct count_info info; - assert(u && u->source); - - if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) { - fprintf(stderr, "SNDCTL_DSP_GETIPTR: %s\n", strerror(errno)); - return; - } - - if (!info.blocks) - return; - - in_post_memblocks(u, info.blocks); - in_clear_memblocks(u, u->in_fragments/2); -}; - -static void io_callback(struct pa_mainloop_api *m, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { - struct userdata *u = userdata; - - assert (u && u->core->mainloop == m && u->mainloop_source == id); - - if (events & PA_MAINLOOP_API_IO_EVENT_INPUT) - do_read(u); - if (events & PA_MAINLOOP_API_IO_EVENT_OUTPUT) - do_write(u); -} - -static uint32_t sink_get_latency_cb(struct pa_sink *s) { - struct userdata *u = s->userdata; - assert(s && u); - - do_write(u); - return pa_samples_usec(u->out_fill, &s->sample_spec); -} - -int pa_module_init(struct pa_core *c, struct pa_module*m) { - struct audio_buf_info info; - struct userdata *u = NULL; - const char *p; - int nfrags, frag_size; - int mode, caps; - int enable_bits = 0, zero = 0; - int playback = 1, record = 1; - struct pa_modargs *ma = NULL; - assert(c && m); - - m->userdata = u = malloc(sizeof(struct userdata)); - assert(u); - memset(u, 0, sizeof(struct userdata)); - u->fd = -1; - u->core = c; - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - fprintf(stderr, __FILE__": failed to parse module arguments.\n"); - goto fail; - } - - if (pa_modargs_get_value_u32(ma, "record", &record) < 0 || pa_modargs_get_value_u32(ma, "playback", &playback) < 0) { - fprintf(stderr, __FILE__": record= and playback= expect numeric arguments.\n"); - goto fail; - } - - mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0)); - if (mode == 0) { - fprintf(stderr, __FILE__": neither playback nor record enabled for device.\n"); - goto fail; - } - - nfrags = 12; - frag_size = 1024; - if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || nfrags < 2 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 || frag_size < 1) { - fprintf(stderr, __FILE__": failed to parse fragments arguments\n"); - goto fail; - } - - u->sample_spec = c->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &u->sample_spec) < 0) { - fprintf(stderr, __FILE__": failed to parse sample specification\n"); - goto fail; - } - - if ((u->fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0) - goto fail; - - if (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_REALTIME) || !(caps & DSP_CAP_TRIGGER)) { - fprintf(stderr, "OSS device not mmap capable.\n"); - goto fail; - } - - fprintf(stderr, "module-oss: device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR")); - - if (pa_oss_set_fragments(u->fd, nfrags, frag_size) < 0) - goto fail; - - if (pa_oss_auto_format(u->fd, &u->sample_spec) < 0) - goto fail; - - if (mode != O_WRONLY) { - if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) { - fprintf(stderr, "SNDCTL_DSP_GETISPACE: %s\n", strerror(errno)); - goto fail; - } - - fprintf(stderr, "module-oss-mmap: input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); - u->in_mmap_length = (u->in_fragment_size = info.fragsize) * (u->in_fragments = info.fragstotal); - - if ((u->in_mmap = mmap(NULL, u->in_mmap_length, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) { - if (mode == O_RDWR) { - fprintf(stderr, "module-oss-mmap: mmap failed for input. Changing to O_WRONLY mode.\n"); - mode = O_WRONLY; - } else { - fprintf(stderr, "modeule-oss-mmap: mmap(): %s\n", strerror(errno)); - goto fail; - } - } else { - - u->source = pa_source_new(c, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &u->sample_spec); - assert(u->source); - u->source->userdata = u; - pa_source_set_owner(u->source, m); - u->source->description = pa_sprintf_malloc("Open Sound System PCM/mmap() on '%s'", p); - - - u->in_memblocks = malloc(sizeof(struct pa_memblock *)*u->in_fragments); - memset(u->in_memblocks, 0, sizeof(struct pa_memblock *)*u->in_fragments); - - enable_bits |= PCM_ENABLE_INPUT; - } - } - - if (mode != O_RDONLY) { - if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { - fprintf(stderr, "SNDCTL_DSP_GETOSPACE: %s\n", strerror(errno)); - goto fail; - } - - fprintf(stderr, "module-oss: output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); - u->out_mmap_length = (u->out_fragment_size = info.fragsize) * (u->out_fragments = info.fragstotal); - - if ((u->out_mmap = mmap(NULL, u->out_mmap_length, PROT_WRITE, MAP_SHARED, u->fd, 0)) == MAP_FAILED) { - if (mode == O_RDWR) { - fprintf(stderr, "module-oss-mmap: mmap filed for output. Changing to O_RDONLY mode.\n"); - mode = O_RDONLY; - } else { - fprintf(stderr, "module-oss-mmap: mmap(): %s\n", strerror(errno)); - goto fail; - } - } else { - pa_silence_memory(u->out_mmap, u->out_mmap_length, &u->sample_spec); - - u->sink = pa_sink_new(c, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &u->sample_spec); - assert(u->sink); - u->sink->get_latency = sink_get_latency_cb; - u->sink->userdata = u; - pa_sink_set_owner(u->sink, m); - u->sink->description = pa_sprintf_malloc("Open Sound System PCM/mmap() on '%s'", p); - - u->out_memblocks = malloc(sizeof(struct memblock *)*u->out_fragments); - memset(u->out_memblocks, 0, sizeof(struct pa_memblock *)*u->out_fragments); - - enable_bits |= PCM_ENABLE_OUTPUT; - } - } - - zero = 0; - if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero) < 0) { - fprintf(stderr, "SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno)); - goto fail; - } - - if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) { - fprintf(stderr, "SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno)); - goto fail; - } - - assert(u->source || u->sink); - - u->mainloop_source = c->mainloop->source_io(c->mainloop, u->fd, (u->source ? PA_MAINLOOP_API_IO_EVENT_INPUT : 0) | (u->sink ? PA_MAINLOOP_API_IO_EVENT_OUTPUT : 0), io_callback, u); - assert(u->mainloop_source); - - pa_modargs_free(ma); - - return 0; - -fail: - pa_module_done(c, m); - - if (ma) - pa_modargs_free(ma); - - return -1; -} - -void pa_module_done(struct pa_core *c, struct pa_module*m) { - struct userdata *u; - assert(c && m); - - u = m->userdata; - assert(u); - - if (u->out_memblocks) { - unsigned i; - for (i = 0; i < u->out_fragments; i++) - if (u->out_memblocks[i]) - pa_memblock_unref_fixed(u->out_memblocks[i]); - free(u->out_memblocks); - } - - if (u->in_memblocks) { - unsigned i; - for (i = 0; i < u->in_fragments; i++) - if (u->in_memblocks[i]) - pa_memblock_unref_fixed(u->in_memblocks[i]); - free(u->in_memblocks); - } - - if (u->in_mmap && u->in_mmap != MAP_FAILED) - munmap(u->in_mmap, u->in_mmap_length); - - if (u->out_mmap && u->out_mmap != MAP_FAILED) - munmap(u->out_mmap, u->out_mmap_length); - - if (u->sink) - pa_sink_free(u->sink); - - if (u->source) - pa_source_free(u->source); - - if (u->mainloop_source) - u->core->mainloop->cancel_io(u->core->mainloop, u->mainloop_source); - - if (u->fd >= 0) - close(u->fd); - - free(u); -} diff --git a/src/module-oss.c b/src/module-oss.c deleted file mode 100644 index d727534a..00000000 --- a/src/module-oss.c +++ /dev/null @@ -1,304 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#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" - -struct userdata { - struct pa_sink *sink; - struct pa_source *source; - struct pa_iochannel *io; - struct pa_core *core; - - struct pa_memchunk memchunk, silence; - - uint32_t in_fragment_size, out_fragment_size, sample_size; - - int fd; -}; - -static const char* const valid_modargs[] = { - "sink_name", - "source_name", - "device", - "record", - "playback", - "fragments", - "fragment_size", - "format", - "rate", - "channels", - NULL -}; - -#define DEFAULT_SINK_NAME "oss_output" -#define DEFAULT_SOURCE_NAME "oss_input" -#define DEFAULT_DEVICE "/dev/dsp" - -static void do_write(struct userdata *u) { - struct pa_memchunk *memchunk; - ssize_t r; - assert(u); - - if (!u->sink || !pa_iochannel_is_writable(u->io)) - return; - - if (!u->memchunk.length) { - if (pa_sink_render(u->sink, u->out_fragment_size, &u->memchunk) < 0) - memchunk = &u->silence; - else - memchunk = &u->memchunk; - } - - assert(memchunk->memblock && memchunk->length); - - if ((r = pa_iochannel_write(u->io, memchunk->memblock->data + memchunk->index, memchunk->length)) < 0) { - fprintf(stderr, "write() failed: %s\n", strerror(errno)); - return; - } - - if (memchunk == &u->silence) - assert(r % u->sample_size == 0); - else { - u->memchunk.index += r; - u->memchunk.length -= r; - - if (u->memchunk.length <= 0) { - pa_memblock_unref(u->memchunk.memblock); - u->memchunk.memblock = NULL; - } - } -} - -static void do_read(struct userdata *u) { - struct pa_memchunk memchunk; - ssize_t r; - assert(u); - - if (!u->source || !pa_iochannel_is_readable(u->io)) - return; - - memchunk.memblock = pa_memblock_new(u->in_fragment_size); - assert(memchunk.memblock); - if ((r = pa_iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) { - pa_memblock_unref(memchunk.memblock); - if (errno != EAGAIN) - fprintf(stderr, "read() failed: %s\n", strerror(errno)); - return; - } - - assert(r <= (ssize_t) memchunk.memblock->length); - memchunk.length = memchunk.memblock->length = r; - memchunk.index = 0; - - pa_source_post(u->source, &memchunk); - pa_memblock_unref(memchunk.memblock); -}; - -static void io_callback(struct pa_iochannel *io, void*userdata) { - struct userdata *u = userdata; - assert(u); - do_write(u); - do_read(u); -} - -static uint32_t sink_get_latency_cb(struct pa_sink *s) { - int arg; - struct userdata *u = s->userdata; - assert(s && u && u->sink); - - if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) { - fprintf(stderr, "module-oss: device doesn't support SNDCTL_DSP_GETODELAY.\n"); - s->get_latency = NULL; - return 0; - } - - return pa_samples_usec(arg, &s->sample_spec); -} - -int pa_module_init(struct pa_core *c, struct pa_module*m) { - struct audio_buf_info info; - struct userdata *u = NULL; - const char *p; - int fd = -1; - int nfrags, frag_size, in_frag_size, out_frag_size; - int mode; - uint32_t record = 1, playback = 1; - struct pa_sample_spec ss; - struct pa_modargs *ma = NULL; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - fprintf(stderr, __FILE__": failed to parse module arguments.\n"); - goto fail; - } - - if (pa_modargs_get_value_u32(ma, "record", &record) < 0 || pa_modargs_get_value_u32(ma, "playback", &playback) < 0) { - fprintf(stderr, __FILE__": record= and playback= expect numeric argument.\n"); - goto fail; - } - - mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0)); - if (mode == 0) { - fprintf(stderr, __FILE__": neither playback nor record enabled for device.\n"); - goto fail; - } - - nfrags = 12; - frag_size = 1024; - if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || nfrags < 2 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 || frag_size < 1) { - fprintf(stderr, __FILE__": failed to parse fragments arguments\n"); - goto fail; - } - - ss = c->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &ss) < 0) { - fprintf(stderr, __FILE__": failed to parse sample specification\n"); - goto fail; - } - - if ((fd = pa_oss_open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, NULL)) < 0) - goto fail; - - fprintf(stderr, "module-oss: device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR")); - - if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0) - goto fail; - - if (pa_oss_auto_format(fd, &ss) < 0) - goto fail; - - if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) { - fprintf(stderr, "SNDCTL_DSP_GETBLKSIZE: %s\n", strerror(errno)); - goto fail; - } - assert(frag_size); - in_frag_size = out_frag_size = frag_size; - - if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) { - fprintf(stderr, "module-oss: input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); - in_frag_size = info.fragsize; - } - - if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) { - fprintf(stderr, "module-oss: output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); - out_frag_size = info.fragsize; - } - - u = malloc(sizeof(struct userdata)); - assert(u); - - u->core = c; - - if (mode != O_WRONLY) { - u->source = pa_source_new(c, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss); - assert(u->source); - u->source->userdata = u; - pa_source_set_owner(u->source, m); - u->source->description = pa_sprintf_malloc("Open Sound System PCM on '%s'", p); - } else - u->source = NULL; - - if (mode != O_RDONLY) { - u->sink = pa_sink_new(c, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss); - assert(u->sink); - u->sink->get_latency = sink_get_latency_cb; - u->sink->userdata = u; - pa_sink_set_owner(u->sink, m); - u->sink->description = pa_sprintf_malloc("Open Sound System PCM on '%s'", p); - } else - u->sink = NULL; - - assert(u->source || u->sink); - - u->io = pa_iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0); - assert(u->io); - pa_iochannel_set_callback(u->io, io_callback, u); - u->fd = fd; - - u->memchunk.memblock = NULL; - u->memchunk.length = 0; - u->sample_size = pa_sample_size(&ss); - - u->out_fragment_size = out_frag_size; - u->in_fragment_size = in_frag_size; - u->silence.memblock = pa_memblock_new(u->silence.length = u->out_fragment_size); - assert(u->silence.memblock); - pa_silence_memblock(u->silence.memblock, &ss); - u->silence.index = 0; - - m->userdata = u; - - pa_modargs_free(ma); - - return 0; - -fail: - if (fd >= 0) - close(fd); - - if (ma) - pa_modargs_free(ma); - - return -1; -} - -void pa_module_done(struct pa_core *c, struct pa_module*m) { - struct userdata *u; - assert(c && m); - - u = m->userdata; - assert(u); - - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); - if (u->silence.memblock) - pa_memblock_unref(u->silence.memblock); - - if (u->sink) - pa_sink_free(u->sink); - if (u->source) - pa_source_free(u->source); - pa_iochannel_free(u->io); - free(u); -} diff --git a/src/module-pipe-sink.c b/src/module-pipe-sink.c deleted file mode 100644 index df34f73a..00000000 --- a/src/module-pipe-sink.c +++ /dev/null @@ -1,218 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#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" - -#define DEFAULT_FIFO_NAME "/tmp/musicfifo" -#define DEFAULT_SINK_NAME "fifo_output" - -struct userdata { - struct pa_core *core; - - char *filename; - - struct pa_sink *sink; - struct pa_iochannel *io; - void *mainloop_source; - - struct pa_memchunk memchunk; -}; - -static const char* const valid_modargs[] = { - "file", - "rate", - "channels", - "format", - "sink_name", - NULL -}; - -static void do_write(struct userdata *u) { - ssize_t r; - assert(u); - - u->core->mainloop->enable_fixed(u->core->mainloop, u->mainloop_source, 0); - - if (!pa_iochannel_is_writable(u->io)) - return; - - if (!u->memchunk.length) - if (pa_sink_render(u->sink, PIPE_BUF, &u->memchunk) < 0) - return; - - assert(u->memchunk.memblock && u->memchunk.length); - - if ((r = pa_iochannel_write(u->io, u->memchunk.memblock->data + u->memchunk.index, u->memchunk.length)) < 0) { - fprintf(stderr, "write() failed: %s\n", strerror(errno)); - return; - } - - u->memchunk.index += r; - u->memchunk.length -= r; - - if (u->memchunk.length <= 0) { - pa_memblock_unref(u->memchunk.memblock); - u->memchunk.memblock = NULL; - } -} - -static void notify_cb(struct pa_sink*s) { - struct userdata *u = s->userdata; - assert(s && u); - - if (pa_iochannel_is_writable(u->io)) - u->core->mainloop->enable_fixed(u->core->mainloop, u->mainloop_source, 1); -} - -static void fixed_callback(struct pa_mainloop_api *m, void *id, void *userdata) { - struct userdata *u = userdata; - assert(u); - do_write(u); -} - -static void io_callback(struct pa_iochannel *io, void*userdata) { - struct userdata *u = userdata; - assert(u); - do_write(u); -} - -int pa_module_init(struct pa_core *c, struct pa_module*m) { - struct userdata *u = NULL; - struct stat st; - const char *p; - int fd = -1; - struct pa_sample_spec ss; - struct pa_modargs *ma = NULL; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - fprintf(stderr, __FILE__": failed to parse module arguments\n"); - goto fail; - } - - ss = c->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &ss) < 0) { - fprintf(stderr, __FILE__": invalid sample format specification\n"); - goto fail; - } - - mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777); - - if ((fd = open(p, O_RDWR)) < 0) { - fprintf(stderr, __FILE__": open('%s'): %s\n", p, strerror(errno)); - goto fail; - } - - if (fstat(fd, &st) < 0) { - fprintf(stderr, __FILE__": fstat('%s'): %s\n", p, strerror(errno)); - goto fail; - } - - if (!S_ISFIFO(st.st_mode)) { - fprintf(stderr, __FILE__": '%s' is not a FIFO.\n", p); - goto fail; - } - - u = malloc(sizeof(struct userdata)); - assert(u); - memset(u, 0, sizeof(struct userdata)); - - u->filename = strdup(p); - assert(u->filename); - u->core = c; - - if (!(u->sink = pa_sink_new(c, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss))) { - fprintf(stderr, __FILE__": failed to create sink.\n"); - goto fail; - } - u->sink->notify = notify_cb; - u->sink->userdata = u; - pa_sink_set_owner(u->sink, m); - u->sink->description = pa_sprintf_malloc("Unix FIFO sink '%s'", p); - assert(u->sink->description); - - u->io = pa_iochannel_new(c->mainloop, -1, fd); - assert(u->io); - pa_iochannel_set_callback(u->io, io_callback, u); - - u->memchunk.memblock = NULL; - u->memchunk.length = 0; - - u->mainloop_source = c->mainloop->source_fixed(c->mainloop, fixed_callback, u); - assert(u->mainloop_source); - c->mainloop->enable_fixed(c->mainloop, u->mainloop_source, 0); - - m->userdata = u; - - pa_modargs_free(ma); - - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - if (fd >= 0) - close(fd); - - pa_module_done(c, m); - - return -1; -} - -void pa_module_done(struct pa_core *c, struct pa_module*m) { - struct userdata *u; - assert(c && m); - - if (!(u = m->userdata)) - return; - - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); - - pa_sink_free(u->sink); - pa_iochannel_free(u->io); - u->core->mainloop->cancel_fixed(u->core->mainloop, u->mainloop_source); - - assert(u->filename); - unlink(u->filename); - free(u->filename); - - free(u); -} diff --git a/src/module-protocol-stub.c b/src/module-protocol-stub.c deleted file mode 100644 index 3ce18c31..00000000 --- a/src/module-protocol-stub.c +++ /dev/null @@ -1,165 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include - -#include "module.h" -#include "socket-server.h" -#include "socket-util.h" -#include "util.h" -#include "modargs.h" - -#ifdef USE_PROTOCOL_SIMPLE - #include "protocol-simple.h" - #define protocol_new pa_protocol_simple_new - #define protocol_free pa_protocol_simple_free - #define IPV4_PORT 4711 - #define UNIX_SOCKET "/tmp/polypaudio/simple" - #define MODULE_ARGUMENTS "rate", "format", "channels", "sink", "source", "playback", "record", -#else - #ifdef USE_PROTOCOL_CLI - #include "protocol-cli.h" - #define protocol_new pa_protocol_cli_new - #define protocol_free pa_protocol_cli_free - #define IPV4_PORT 4712 - #define UNIX_SOCKET "/tmp/polypaudio/cli" - #define MODULE_ARGUMENTS - #else - #ifdef USE_PROTOCOL_NATIVE - #include "protocol-native.h" - #define protocol_new pa_protocol_native_new - #define protocol_free pa_protocol_native_free - #define IPV4_PORT 4713 - #define UNIX_SOCKET "/tmp/polypaudio/native" - #define MODULE_ARGUMENTS "public", "cookie", - #else - #ifdef USE_PROTOCOL_ESOUND - #include "protocol-esound.h" - #include "esound.h" - #define protocol_new pa_protocol_esound_new - #define protocol_free pa_protocol_esound_free - #define IPV4_PORT ESD_DEFAULT_PORT - #define UNIX_SOCKET ESD_UNIX_SOCKET_NAME - #define MODULE_ARGUMENTS "sink", "source", "public", "cookie", - #else - #error "Broken build system" - #endif - #endif - #endif -#endif - -static const char* const valid_modargs[] = { - MODULE_ARGUMENTS -#ifdef USE_TCP_SOCKETS - "port", - "loopback", -#else - "socket", -#endif - NULL -}; - -static struct pa_socket_server *create_socket_server(struct pa_core *c, struct pa_modargs *ma) { - struct pa_socket_server *s; -#ifdef USE_TCP_SOCKETS - uint32_t loopback = 1, port = IPV4_PORT; - - if (pa_modargs_get_value_u32(ma, "loopback", &loopback) < 0) { - fprintf(stderr, "loopback= expects a numerical argument.\n"); - return NULL; - } - - if (pa_modargs_get_value_u32(ma, "port", &port) < 0) { - fprintf(stderr, "port= expects a numerical argument.\n"); - return NULL; - } - - if (!(s = pa_socket_server_new_ipv4(c->mainloop, loopback ? INADDR_LOOPBACK : INADDR_ANY, port))) - return NULL; -#else - int r; - const char *p; - - p = pa_modargs_get_value(ma, "socket", UNIX_SOCKET); - assert(p); - - if (pa_unix_socket_make_secure_dir(p) < 0) { - fprintf(stderr, "Failed to create secure socket directory.\n"); - return NULL; - } - - if ((r = pa_unix_socket_remove_stale(p)) < 0) { - fprintf(stderr, "Failed to remove stale UNIX socket '%s': %s\n", p, strerror(errno)); - return NULL; - } - - if (r) - fprintf(stderr, "Removed stale UNIX socket '%s'.", p); - - if (!(s = pa_socket_server_new_unix(c->mainloop, p))) - return NULL; - -#endif - return s; -} - -int pa_module_init(struct pa_core *c, struct pa_module*m) { - struct pa_socket_server *s; - struct pa_modargs *ma = NULL; - int ret = -1; - assert(c && m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - fprintf(stderr, "Failed to parse module arguments\n"); - goto finish; - } - - if (!(s = create_socket_server(c, ma))) - goto finish; - - if (!(m->userdata = protocol_new(c, s, m, ma))) { - pa_socket_server_free(s); - goto finish; - } - - ret = 0; - -finish: - if (ma) - pa_modargs_free(ma); - - return ret; -} - -void pa_module_done(struct pa_core *c, struct pa_module*m) { - assert(c && m); - - protocol_free(m->userdata); -} diff --git a/src/module.c b/src/module.c deleted file mode 100644 index 5c6f0fb6..00000000 --- a/src/module.c +++ /dev/null @@ -1,161 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include - -#include "module.h" - -struct pa_module* pa_module_load(struct pa_core *c, const char *name, const char *argument) { - struct pa_module *m = NULL; - int r; - - assert(c && name); - - m = malloc(sizeof(struct pa_module)); - assert(m); - - m->name = strdup(name); - m->argument = argument ? strdup(argument) : NULL; - - if (!(m->dl = lt_dlopenext(name))) - goto fail; - - if (!(m->init = lt_dlsym(m->dl, "pa_module_init"))) - goto fail; - - if (!(m->done = lt_dlsym(m->dl, "pa_module_done"))) - goto fail; - - m->userdata = NULL; - m->core = c; - - assert(m->init); - if (m->init(c, m) < 0) - goto fail; - - if (!c->modules) - c->modules = pa_idxset_new(NULL, NULL); - - assert(c->modules); - r = pa_idxset_put(c->modules, m, &m->index); - assert(r >= 0 && m->index != PA_IDXSET_INVALID); - - fprintf(stderr, "module: loaded %u \"%s\" with argument \"%s\".\n", m->index, m->name, m->argument); - - return m; - -fail: - if (m) { - free(m->argument); - free(m->name); - - if (m->dl) - lt_dlclose(m->dl); - - free(m); - } - - return NULL; -} - -static void pa_module_free(struct pa_module *m) { - assert(m && m->done && m->core); - m->done(m->core, m); - - lt_dlclose(m->dl); - - fprintf(stderr, "module: unloaded %u \"%s\".\n", m->index, m->name); - - free(m->name); - free(m->argument); - free(m); -} - - -void pa_module_unload(struct pa_core *c, struct pa_module *m) { - assert(c && m); - - assert(c->modules); - if (!(m = pa_idxset_remove_by_data(c->modules, m, NULL))) - return; - - pa_module_free(m); -} - -void pa_module_unload_by_index(struct pa_core *c, uint32_t index) { - struct pa_module *m; - assert(c && index != PA_IDXSET_INVALID); - - assert(c->modules); - if (!(m = pa_idxset_remove_by_index(c->modules, index))) - return; - - pa_module_free(m); -} - -static void free_callback(void *p, void *userdata) { - struct pa_module *m = p; - assert(m); - pa_module_free(m); -} - -void pa_module_unload_all(struct pa_core *c) { - assert(c); - - if (!c->modules) - return; - - pa_idxset_free(c->modules, free_callback, NULL); - c->modules = NULL; -} - -struct once_info { - struct pa_core *core; - uint32_t index; -}; - - -static void module_unload_once_callback(void *userdata) { - struct once_info *i = userdata; - assert(i); - pa_module_unload_by_index(i->core, i->index); - free(i); -} - -void pa_module_unload_request(struct pa_core *c, struct pa_module *m) { - struct once_info *i; - assert(c && m); - - i = malloc(sizeof(struct once_info)); - assert(i); - i->core = c; - i->index = m->index; - pa_mainloop_api_once(c->mainloop, module_unload_once_callback, i); -} diff --git a/src/module.h b/src/module.h deleted file mode 100644 index af2d8552..00000000 --- a/src/module.h +++ /dev/null @@ -1,55 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include - -#include "core.h" - -struct pa_module { - struct pa_core *core; - char *name, *argument; - uint32_t index; - - lt_dlhandle dl; - - int (*init)(struct pa_core *c, struct pa_module*m); - void (*done)(struct pa_core *c, struct pa_module*m); - - void *userdata; -}; - -struct pa_module* pa_module_load(struct pa_core *c, const char *name, const char*argument); -void pa_module_unload(struct pa_core *c, struct pa_module *m); -void pa_module_unload_by_index(struct pa_core *c, uint32_t index); - -void pa_module_unload_all(struct pa_core *c); - -void pa_module_unload_request(struct pa_core *c, struct pa_module *m); - -/* These to following prototypes are for module entrypoints and not implemented by the core */ -int pa_module_init(struct pa_core *c, struct pa_module*m); -void pa_module_done(struct pa_core *c, struct pa_module*m); - -#endif diff --git a/src/namereg.c b/src/namereg.c deleted file mode 100644 index 2349436f..00000000 --- a/src/namereg.c +++ /dev/null @@ -1,136 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include - -#include "namereg.h" - -struct namereg_entry { - enum pa_namereg_type type; - char *name; - void *data; -}; - -void pa_namereg_free(struct pa_core *c) { - assert(c); - if (!c->namereg) - return; - assert(pa_hashmap_ncontents(c->namereg) == 0); - pa_hashmap_free(c->namereg, NULL, NULL); -} - -const char *pa_namereg_register(struct pa_core *c, const char *name, enum pa_namereg_type type, void *data, int fail) { - struct namereg_entry *e; - char *n = NULL; - int r; - - assert(c && name && data); - - if (!c->namereg) { - c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - assert(c->namereg); - } - - if ((e = pa_hashmap_get(c->namereg, name)) && fail) - return NULL; - - if (!e) - n = strdup(name); - else { - unsigned i; - size_t l = strlen(name); - n = malloc(l+3); - assert(n); - - for (i = 1; i <= 99; i++) { - snprintf(n, l+2, "%s%u", name, i); - - if (!(e = pa_hashmap_get(c->namereg, n))) - break; - } - - if (e) { - free(n); - return NULL; - } - } - - assert(n); - e = malloc(sizeof(struct namereg_entry)); - assert(e); - e->type = type; - e->name = n; - e->data = data; - - r = pa_hashmap_put(c->namereg, e->name, e); - assert (r >= 0); - - return e->name; - -} - -void pa_namereg_unregister(struct pa_core *c, const char *name) { - struct namereg_entry *e; - int r; - assert(c && name); - - e = pa_hashmap_get(c->namereg, name); - assert(e); - - r = pa_hashmap_remove(c->namereg, name); - assert(r >= 0); - - free(e->name); - free(e); -} - -void* pa_namereg_get(struct pa_core *c, const char *name, enum pa_namereg_type type) { - struct namereg_entry *e; - uint32_t index; - char *x = NULL; - void *d = NULL; - assert(c && name); - - if ((e = pa_hashmap_get(c->namereg, name))) - if (e->type == e->type) - return e->data; - - index = (uint32_t) strtol(name, &x, 0); - - if (!x || *x != 0) - return NULL; - - if (type == PA_NAMEREG_SINK) - d = pa_idxset_get_by_index(c->sinks, index); - else if (type == PA_NAMEREG_SOURCE) - d = pa_idxset_get_by_index(c->sources, index); - - return d; -} diff --git a/src/namereg.h b/src/namereg.h deleted file mode 100644 index 0af83cd8..00000000 --- a/src/namereg.h +++ /dev/null @@ -1,38 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "core.h" - -enum pa_namereg_type { - PA_NAMEREG_SINK, - PA_NAMEREG_SOURCE -}; - -void pa_namereg_free(struct pa_core *c); - -const char *pa_namereg_register(struct pa_core *c, const char *name, enum pa_namereg_type type, void *data, int fail); -void pa_namereg_unregister(struct pa_core *c, const char *name); -void* pa_namereg_get(struct pa_core *c, const char *name, enum pa_namereg_type type); - -#endif diff --git a/src/native-common.h b/src/native-common.h deleted file mode 100644 index 2acbae8e..00000000 --- a/src/native-common.h +++ /dev/null @@ -1,68 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -enum { - PA_COMMAND_ERROR, - PA_COMMAND_TIMEOUT, /* pseudo command */ - PA_COMMAND_REPLY, - PA_COMMAND_CREATE_PLAYBACK_STREAM, - PA_COMMAND_DELETE_PLAYBACK_STREAM, - PA_COMMAND_CREATE_RECORD_STREAM, - PA_COMMAND_DELETE_RECORD_STREAM, - PA_COMMAND_EXIT, - PA_COMMAND_REQUEST, - PA_COMMAND_AUTH, - PA_COMMAND_SET_NAME, - PA_COMMAND_LOOKUP_SINK, - PA_COMMAND_LOOKUP_SOURCE, - PA_COMMAND_DRAIN_PLAYBACK_STREAM, - PA_COMMAND_PLAYBACK_STREAM_KILLED, - PA_COMMAND_RECORD_STREAM_KILLED, - PA_COMMAND_STAT, - PA_COMMAND_GET_PLAYBACK_LATENCY, - PA_COMMAND_MAX -}; - -enum { - PA_ERROR_OK, - PA_ERROR_ACCESS, - PA_ERROR_COMMAND, - PA_ERROR_INVALID, - PA_ERROR_EXIST, - PA_ERROR_NOENTITY, - PA_ERROR_CONNECTIONREFUSED, - PA_ERROR_PROTOCOL, - PA_ERROR_TIMEOUT, - PA_ERROR_AUTHKEY, - PA_ERROR_INTERNAL, - PA_ERROR_CONNECTIONTERMINATED, - PA_ERROR_KILLED, - PA_ERROR_INVALIDSERVER, - PA_ERROR_MAX -}; - -#define PA_NATIVE_COOKIE_LENGTH 256 -#define PA_NATIVE_COOKIE_FILE ".polypaudio-cookie" - -#endif diff --git a/src/oss-util.c b/src/oss-util.c deleted file mode 100644 index cf55a6ee..00000000 --- a/src/oss-util.c +++ /dev/null @@ -1,163 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "oss-util.h" - -int pa_oss_open(const char *device, int *mode, int* pcaps) { - int fd = -1; - assert(device && mode && (*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY)); - - if (*mode == O_RDWR) { - if ((fd = open(device, O_RDWR|O_NDELAY)) >= 0) { - int dcaps, *tcaps; - ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0); - - tcaps = pcaps ? pcaps : &dcaps; - - if (ioctl(fd, SNDCTL_DSP_GETCAPS, tcaps) < 0) { - fprintf(stderr, __FILE__": SNDCTL_DSP_GETCAPS: %s\n", strerror(errno)); - goto fail; - } - - if (*tcaps & DSP_CAP_DUPLEX) - return fd; - - close(fd); - } - - if ((fd = open(device, (*mode = O_WRONLY)|O_NDELAY)) < 0) { - if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY)) < 0) { - fprintf(stderr, __FILE__": open('%s'): %s\n", device, strerror(errno)); - goto fail; - } - } - } else { - if ((fd = open(device, *mode|O_NDELAY)) < 0) { - fprintf(stderr, __FILE__": open('%s'): %s\n", device, strerror(errno)); - goto fail; - } - } - - if (pcaps) { - if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) { - fprintf(stderr, "SNDCTL_DSP_GETCAPS: %s\n", strerror(errno)); - goto fail; - } - } - - return fd; - -fail: - if (fd >= 0) - close(fd); - return fd; -} - -int pa_oss_auto_format(int fd, struct pa_sample_spec *ss) { - int format, channels, speed, reqformat; - static const int format_trans[] = { - [PA_SAMPLE_U8] = AFMT_U8, - [PA_SAMPLE_ALAW] = AFMT_A_LAW, - [PA_SAMPLE_ULAW] = AFMT_MU_LAW, - [PA_SAMPLE_S16LE] = AFMT_S16_LE, - [PA_SAMPLE_S16BE] = AFMT_S16_BE, - [PA_SAMPLE_FLOAT32LE] = AFMT_QUERY, /* not supported */ - [PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */ - }; - - assert(fd >= 0 && ss); - - reqformat = format = format_trans[ss->format]; - if (reqformat == AFMT_QUERY || ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != reqformat) { - format = AFMT_S16_NE; - if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) { - int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE; - format = f; - if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) { - format = AFMT_U8; - if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) { - fprintf(stderr, "SNDCTL_DSP_SETFMT: %s\n", format != AFMT_U8 ? "No supported sample format" : strerror(errno)); - return -1; - } else - ss->format = PA_SAMPLE_U8; - } else - ss->format = f == AFMT_S16_LE ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE; - } else - ss->format = PA_SAMPLE_S16NE; - } - - channels = ss->channels; - if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) { - fprintf(stderr, "SNDCTL_DSP_CHANNELS: %s\n", strerror(errno)); - return -1; - } - assert(channels); - ss->channels = channels; - - speed = ss->rate; - if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) { - fprintf(stderr, "SNDCTL_DSP_SPEED: %s\n", strerror(errno)); - return -1; - } - assert(speed); - ss->rate = speed; - - return 0; -} - -static int log2(int v) { - int k = 0; - - for (;;) { - v >>= 1; - if (!v) break; - k++; - } - - return k; -} - -int pa_oss_set_fragments(int fd, int nfrags, int frag_size) { - int arg; - arg = ((int) nfrags << 16) | log2(frag_size); - - if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) { - fprintf(stderr, "SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno)); - return -1; - } - - return 0; -} diff --git a/src/oss-util.h b/src/oss-util.h deleted file mode 100644 index cb2ce33c..00000000 --- a/src/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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "sample.h" - -int pa_oss_open(const char *device, int *mode, int* pcaps); -int pa_oss_auto_format(int fd, struct pa_sample_spec *ss); - -int pa_oss_set_fragments(int fd, int frags, int frag_size); - -#endif diff --git a/src/pacat-simple.c b/src/pacat-simple.c deleted file mode 100644 index 5018aa5f..00000000 --- a/src/pacat-simple.c +++ /dev/null @@ -1,83 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include "polyplib-simple.h" -#include "polyplib-error.h" - -#define BUFSIZE 1024 - -int main(int argc, char*argv[]) { - static const struct pa_sample_spec ss = { - .format = PA_SAMPLE_S16LE, - .rate = 44100, - .channels = 2 - }; - struct pa_simple *s = NULL; - int ret = 1; - int error; - - if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, &error))) { - fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); - goto finish; - } - - for (;;) { - uint8_t buf[BUFSIZE]; - ssize_t r; - - if ((r = read(STDIN_FILENO, buf, sizeof(buf))) <= 0) { - if (r == 0) /* eof */ - break; - - fprintf(stderr, __FILE__": read() failed: %s\n", strerror(errno)); - goto finish; - } - - if (pa_simple_write(s, buf, r, &error) < 0) { - fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error)); - goto finish; - } - } - - /* Make sure that every single sample way played */ - if (pa_simple_drain(s, &error) < 0) { - fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error)); - goto finish; - } - - ret = 0; - -finish: - - if (s) - pa_simple_free(s); - - return ret; -} diff --git a/src/pacat.c b/src/pacat.c deleted file mode 100644 index 1d1cc3d5..00000000 --- a/src/pacat.c +++ /dev/null @@ -1,336 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "polyplib.h" -#include "polyplib-error.h" -#include "mainloop.h" -#include "mainloop-signal.h" - -static enum { RECORD, PLAYBACK } mode = PLAYBACK; - -static struct pa_context *context = NULL; -static struct pa_stream *stream = NULL; -static struct pa_mainloop_api *mainloop_api = NULL; - -static void *buffer = NULL; -static size_t buffer_length = 0, buffer_index = 0; - -static void* stdio_source = NULL; - -static void quit(int ret) { - assert(mainloop_api); - mainloop_api->quit(mainloop_api, ret); -} - -static void context_die_callback(struct pa_context *c, void *userdata) { - assert(c); - fprintf(stderr, "Connection to server shut down, exiting.\n"); - quit(1); -} - -static void stream_die_callback(struct pa_stream *s, void *userdata) { - assert(s); - fprintf(stderr, "Stream deleted, exiting.\n"); - quit(1); -} - -static void do_stream_write(size_t length) { - size_t l; - assert(length); - - if (!buffer || !buffer_length) - return; - - l = length; - if (l > buffer_length) - l = buffer_length; - - pa_stream_write(stream, buffer+buffer_index, l); - buffer_length -= l; - buffer_index += l; - - if (!buffer_length) { - free(buffer); - buffer = NULL; - buffer_index = buffer_length = 0; - } -} - -static void stream_write_callback(struct pa_stream *s, size_t length, void *userdata) { - assert(s && length); - - if (stdio_source) - mainloop_api->enable_io(mainloop_api, stdio_source, PA_MAINLOOP_API_IO_EVENT_INPUT); - - if (!buffer) - return; - - do_stream_write(length); -} - -static void stream_read_callback(struct pa_stream *s, const void*data, size_t length, void *userdata) { - assert(s && data && length); - - if (stdio_source) - mainloop_api->enable_io(mainloop_api, stdio_source, PA_MAINLOOP_API_IO_EVENT_OUTPUT); - - if (buffer) { - fprintf(stderr, "Buffer overrrun, dropping incoming data\n"); - return; - } - - buffer = malloc(buffer_length = length); - assert(buffer); - memcpy(buffer, data, length); - buffer_index = 0; -} - -static void stream_complete_callback(struct pa_stream*s, int success, void *userdata) { - assert(s); - - if (!success) { - fprintf(stderr, "Stream creation failed: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s)))); - quit(1); - return; - } - - fprintf(stderr, "Stream created.\n"); -} - -static void context_complete_callback(struct pa_context *c, int success, void *userdata) { - static const struct pa_sample_spec ss = { - .format = PA_SAMPLE_S16LE, - .rate = 44100, - .channels = 2 - }; - - assert(c && !stream); - - if (!success) { - fprintf(stderr, "Connection failed: %s\n", pa_strerror(pa_context_errno(c))); - goto fail; - } - - fprintf(stderr, "Connection established.\n"); - - if (!(stream = pa_stream_new(c, mode == PLAYBACK ? PA_STREAM_PLAYBACK : PA_STREAM_RECORD, NULL, "pacat", &ss, NULL, stream_complete_callback, NULL))) { - fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(c))); - goto fail; - } - - pa_stream_set_die_callback(stream, stream_die_callback, NULL); - pa_stream_set_write_callback(stream, stream_write_callback, NULL); - pa_stream_set_read_callback(stream, stream_read_callback, NULL); - - return; - -fail: - quit(1); -} - -static void context_drain_complete(struct pa_context*c, void *userdata) { - quit(0); -} - -static void stream_drain_complete(struct pa_stream*s, void *userdata) { - fprintf(stderr, "Playback stream drained.\n"); - - pa_stream_free(stream); - stream = NULL; - - if (pa_context_drain(context, context_drain_complete, NULL) < 0) - quit(0); - else - fprintf(stderr, "Draining connection to server.\n"); -} - -static void stdin_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { - size_t l, w = 0; - ssize_t r; - assert(a == mainloop_api && id && stdio_source == id); - - if (buffer) { - mainloop_api->enable_io(mainloop_api, stdio_source, PA_MAINLOOP_API_IO_EVENT_NULL); - return; - } - - if (!stream || !pa_stream_is_ready(stream) || !(l = w = pa_stream_writable_size(stream))) - l = 4096; - - buffer = malloc(l); - assert(buffer); - if ((r = read(fd, buffer, l)) <= 0) { - if (r == 0) { - fprintf(stderr, "Got EOF.\n"); - pa_stream_drain(stream, stream_drain_complete, NULL); - } else { - fprintf(stderr, "read() failed: %s\n", strerror(errno)); - quit(1); - } - - mainloop_api->cancel_io(mainloop_api, stdio_source); - stdio_source = NULL; - return; - } - - buffer_length = r; - buffer_index = 0; - - if (w) - do_stream_write(w); -} - -static void stdout_callback(struct pa_mainloop_api*a, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { - ssize_t r; - assert(a == mainloop_api && id && stdio_source == id); - - if (!buffer) { - mainloop_api->enable_io(mainloop_api, stdio_source, PA_MAINLOOP_API_IO_EVENT_NULL); - return; - } - - assert(buffer_length); - - if ((r = write(fd, buffer+buffer_index, buffer_length)) <= 0) { - fprintf(stderr, "write() failed: %s\n", strerror(errno)); - quit(1); - - mainloop_api->cancel_io(mainloop_api, stdio_source); - stdio_source = NULL; - return; - } - - buffer_length -= r; - buffer_index += r; - - if (!buffer_length) { - free(buffer); - buffer = NULL; - buffer_length = buffer_index = 0; - } -} - -static void exit_signal_callback(void *id, int sig, void *userdata) { - fprintf(stderr, "Got SIGINT, exiting.\n"); - quit(0); - -} - -static void stream_get_latency_callback(struct pa_stream *s, uint32_t latency, void *userdata) { - assert(s); - - if (latency == (uint32_t) -1) { - fprintf(stderr, "Failed to get latency: %s\n", strerror(errno)); - quit(1); - return; - } - - fprintf(stderr, "Current latency is %u usecs.\n", latency); -} - -static void sigusr1_signal_callback(void *id, int sig, void *userdata) { - if (mode == PLAYBACK) { - fprintf(stderr, "Got SIGUSR1, requesting latency.\n"); - pa_stream_get_latency(stream, stream_get_latency_callback, NULL); - } -} - -int main(int argc, char *argv[]) { - struct pa_mainloop* m = NULL; - int ret = 1, r; - char *bn; - - if (!(bn = strrchr(argv[0], '/'))) - bn = argv[0]; - - if (strstr(bn, "rec") || strstr(bn, "mon")) - mode = RECORD; - else if (strstr(bn, "cat") || strstr(bn, "play")) - mode = PLAYBACK; - - fprintf(stderr, "Opening a %s stream.\n", mode == RECORD ? "recording" : "playback"); - - if (!(m = pa_mainloop_new())) { - fprintf(stderr, "pa_mainloop_new() failed.\n"); - goto quit; - } - - mainloop_api = pa_mainloop_get_api(m); - - r = pa_signal_init(mainloop_api); - assert(r == 0); - pa_signal_register(SIGINT, exit_signal_callback, NULL); - pa_signal_register(SIGUSR1, sigusr1_signal_callback, NULL); - signal(SIGPIPE, SIG_IGN); - - if (!(stdio_source = mainloop_api->source_io(mainloop_api, - mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO, - mode == PLAYBACK ? PA_MAINLOOP_API_IO_EVENT_INPUT : PA_MAINLOOP_API_IO_EVENT_OUTPUT, - mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) { - fprintf(stderr, "source_io() failed.\n"); - goto quit; - } - - if (!(context = pa_context_new(mainloop_api, argv[0]))) { - fprintf(stderr, "pa_context_new() failed.\n"); - goto quit; - } - - if (pa_context_connect(context, NULL, context_complete_callback, NULL) < 0) { - fprintf(stderr, "pa_context_connext() failed.\n"); - goto quit; - } - - pa_context_set_die_callback(context, context_die_callback, NULL); - - if (pa_mainloop_run(m, &ret) < 0) { - fprintf(stderr, "pa_mainloop_run() failed.\n"); - goto quit; - } - -quit: - if (stream) - pa_stream_free(stream); - if (context) - pa_context_free(context); - - if (m) { - pa_signal_done(); - pa_mainloop_free(m); - } - - if (buffer) - free(buffer); - - return ret; -} diff --git a/src/packet.c b/src/packet.c deleted file mode 100644 index e94df057..00000000 --- a/src/packet.c +++ /dev/null @@ -1,72 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include "packet.h" - -struct pa_packet* pa_packet_new(size_t length) { - struct pa_packet *p; - assert(length); - p = malloc(sizeof(struct pa_packet)+length); - assert(p); - - p->ref = 1; - p->length = length; - p->data = (uint8_t*) (p+1); - p->type = PA_PACKET_APPENDED; - return p; -} - -struct pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length) { - struct pa_packet *p; - assert(data && length); - p = malloc(sizeof(struct pa_packet)); - assert(p); - - p->ref = 1; - p->length = length; - p->data = data; - p->type = PA_PACKET_DYNAMIC; - return p; -} - -struct pa_packet* pa_packet_ref(struct pa_packet *p) { - assert(p && p->ref >= 1); - p->ref++; - return p; -} - -void pa_packet_unref(struct pa_packet *p) { - assert(p && p->ref >= 1); - p->ref--; - - if (p->ref == 0) { - if (p->type == PA_PACKET_DYNAMIC) - free(p->data); - free(p); - } -} diff --git a/src/packet.h b/src/packet.h deleted file mode 100644 index 8bea07e1..00000000 --- a/src/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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include - -struct pa_packet { - enum { PA_PACKET_APPENDED, PA_PACKET_DYNAMIC } type; - unsigned ref; - size_t length; - uint8_t *data; -}; - -struct pa_packet* pa_packet_new(size_t length); -struct pa_packet* pa_packet_new_dynamic(uint8_t* data, size_t length); - -struct pa_packet* pa_packet_ref(struct pa_packet *p); -void pa_packet_unref(struct pa_packet *p); - -#endif diff --git a/src/pactl.c b/src/pactl.c deleted file mode 100644 index 2b1ed2c1..00000000 --- a/src/pactl.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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "polyplib.h" -#include "polyplib-error.h" -#include "mainloop.h" -#include "mainloop-signal.h" - -static struct pa_context *context = NULL; -static struct pa_mainloop_api *mainloop_api = NULL; - -static enum { - NONE, - EXIT, - STAT -} action = NONE; - -static void quit(int ret) { - assert(mainloop_api); - mainloop_api->quit(mainloop_api, ret); -} - -static void context_die_callback(struct pa_context *c, void *userdata) { - assert(c); - fprintf(stderr, "Connection to server shut down, exiting.\n"); - quit(1); -} - -static void context_drain_complete(struct pa_context *c, void *userdata) { - assert(c); - fprintf(stderr, "Connection to server shut down, exiting.\n"); - quit(0); -} - -static void drain(void) { - if (pa_context_drain(context, context_drain_complete, NULL) < 0) - quit(0); -} - -static void stat_callback(struct pa_context *c, uint32_t blocks, uint32_t total, void *userdata) { - if (blocks == (uint32_t) -1) { - fprintf(stderr, "Failed to get statistics: %s\n", pa_strerror(pa_context_errno(c))); - quit(1); - return; - } - - fprintf(stderr, "Currently in use: %u blocks containing %u bytes total.\n", blocks, total); - drain(); -} - -static void context_complete_callback(struct pa_context *c, int success, void *userdata) { - assert(c); - - if (!success) { - fprintf(stderr, "Connection failed: %s\n", pa_strerror(pa_context_errno(c))); - goto fail; - } - - fprintf(stderr, "Connection established.\n"); - - if (action == STAT) - pa_context_stat(c, stat_callback, NULL); - else { - assert(action == EXIT); - pa_context_exit(c); - drain(); - } - - return; - -fail: - quit(1); -} - -static void exit_signal_callback(void *id, int sig, void *userdata) { - fprintf(stderr, "Got SIGINT, exiting.\n"); - quit(0); - -} - -int main(int argc, char *argv[]) { - struct pa_mainloop* m = NULL; - int ret = 1, r; - - if (argc >= 2) { - if (!strcmp(argv[1], "stat")) - action = STAT; - else if (!strcmp(argv[1], "exit")) - action = EXIT; - } - - if (action == NONE) { - fprintf(stderr, "No valid action specified. Use one of: stat, exit\n"); - goto quit; - } - - if (!(m = pa_mainloop_new())) { - fprintf(stderr, "pa_mainloop_new() failed.\n"); - goto quit; - } - - mainloop_api = pa_mainloop_get_api(m); - - r = pa_signal_init(mainloop_api); - assert(r == 0); - pa_signal_register(SIGINT, exit_signal_callback, NULL); - signal(SIGPIPE, SIG_IGN); - - if (!(context = pa_context_new(mainloop_api, argv[0]))) { - fprintf(stderr, "pa_context_new() failed.\n"); - goto quit; - } - - if (pa_context_connect(context, NULL, context_complete_callback, NULL) < 0) { - fprintf(stderr, "pa_context_connext() failed.\n"); - goto quit; - } - - pa_context_set_die_callback(context, context_die_callback, NULL); - - if (pa_mainloop_run(m, &ret) < 0) { - fprintf(stderr, "pa_mainloop_run() failed.\n"); - goto quit; - } - -quit: - if (context) - pa_context_free(context); - - if (m) { - pa_signal_done(); - pa_mainloop_free(m); - } - - return ret; -} diff --git a/src/parec-simple.c b/src/parec-simple.c deleted file mode 100644 index c4196a14..00000000 --- a/src/parec-simple.c +++ /dev/null @@ -1,94 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include "polyplib-simple.h" -#include "polyplib-error.h" - -#define BUFSIZE 1024 - -static ssize_t loop_write(int fd, const void*data, size_t size) { - ssize_t ret = 0; - - while (size > 0) { - ssize_t r; - - if ((r = write(fd, data, size)) < 0) - return r; - - if (r == 0) - break; - - ret += r; - data += r; - size -= r; - } - - return ret; -} - -int main(int argc, char*argv[]) { - static const struct pa_sample_spec ss = { - .format = PA_SAMPLE_S16LE, - .rate = 44100, - .channels = 2 - }; - struct pa_simple *s = NULL; - int ret = 1; - int error; - - if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "record", &ss, NULL, &error))) { - fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); - goto finish; - } - - for (;;) { - uint8_t buf[BUFSIZE]; - ssize_t r; - - if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) { - fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error)); - goto finish; - } - - if ((r = loop_write(STDOUT_FILENO, buf, sizeof(buf))) <= 0) { - fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno)); - goto finish; - } - } - - ret = 0; - -finish: - - if (s) - pa_simple_free(s); - - return ret; -} diff --git a/src/pdispatch.c b/src/pdispatch.c deleted file mode 100644 index a28458a4..00000000 --- a/src/pdispatch.c +++ /dev/null @@ -1,247 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include "pdispatch.h" -#include "native-common.h" - -#ifdef DEBUG_OPCODES - -static const char *command_names[PA_COMMAND_MAX] = { - [PA_COMMAND_ERROR] = "ERROR", - [PA_COMMAND_TIMEOUT] = "TIMEOUT", - [PA_COMMAND_REPLY] = "REPLY", - [PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM", - [PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM", - [PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM", - [PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM", - [PA_COMMAND_AUTH] = "AUTH", - [PA_COMMAND_REQUEST] = "REQUEST", - [PA_COMMAND_EXIT] = "EXIT", - [PA_COMMAND_SET_NAME] = "SET_NAME", - [PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK", - [PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE", - [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM", - [PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED", - [PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED", - [PA_COMMAND_STAT] = "STAT", - [PA_COMMAND_GET_PLAYBACK_LATENCY] = "PLAYBACK_LATENCY", -}; - -#endif - -struct reply_info { - struct pa_pdispatch *pdispatch; - struct reply_info *next, *previous; - void (*callback)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); - void *userdata; - uint32_t tag; - void *mainloop_timeout; - int callback_is_running; -}; - -struct pa_pdispatch { - struct pa_mainloop_api *mainloop; - const struct pa_pdispatch_command *command_table; - unsigned n_commands; - struct reply_info *replies; - void (*drain_callback)(struct pa_pdispatch *pd, void *userdata); - void *drain_userdata; - int in_use, shall_free; -}; - -static void reply_info_free(struct reply_info *r) { - assert(r && r->pdispatch && r->pdispatch->mainloop); - - if (r->pdispatch) - r->pdispatch->mainloop->cancel_time(r->pdispatch->mainloop, r->mainloop_timeout); - - if (r->previous) - r->previous->next = r->next; - else - r->pdispatch->replies = r->next; - - if (r->next) - r->next->previous = r->previous; - - free(r); -} - -struct pa_pdispatch* pa_pdispatch_new(struct pa_mainloop_api *mainloop, const struct pa_pdispatch_command*table, unsigned entries) { - struct pa_pdispatch *pd; - assert(mainloop); - - assert((entries && table) || (!entries && !table)); - - pd = malloc(sizeof(struct pa_pdispatch)); - assert(pd); - pd->mainloop = mainloop; - pd->command_table = table; - pd->n_commands = entries; - pd->replies = NULL; - pd->drain_callback = NULL; - pd->drain_userdata = NULL; - - pd->in_use = pd->shall_free = 0; - return pd; -} - -void pa_pdispatch_free(struct pa_pdispatch *pd) { - assert(pd); - - if (pd->in_use) { - pd->shall_free = 1; - return; - } - - while (pd->replies) - reply_info_free(pd->replies); - free(pd); -} - -int pa_pdispatch_run(struct pa_pdispatch *pd, struct pa_packet*packet, void *userdata) { - uint32_t tag, command; - struct pa_tagstruct *ts = NULL; - int ret = -1; - assert(pd && packet && packet->data && !pd->in_use); - - if (packet->length <= 8) - goto finish; - - ts = pa_tagstruct_new(packet->data, packet->length); - assert(ts); - - if (pa_tagstruct_getu32(ts, &command) < 0 || - pa_tagstruct_getu32(ts, &tag) < 0) - goto finish; - -#ifdef DEBUG_OPCODES - fprintf(stderr, __FILE__": Recieved opcode <%s>\n", command_names[command]); -#endif - - if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) { - struct reply_info *r; - - for (r = pd->replies; r; r = r->next) - if (r->tag == tag) - break; - - if (r) { - pd->in_use = r->callback_is_running = 1; - assert(r->callback); - r->callback(r->pdispatch, command, tag, ts, r->userdata); - pd->in_use = r->callback_is_running = 0; - reply_info_free(r); - - if (pd->shall_free) - pa_pdispatch_free(pd); - else { - if (pd->drain_callback && !pa_pdispatch_is_pending(pd)) - pd->drain_callback(pd, pd->drain_userdata); - } - } - - } else if (pd->command_table && command < pd->n_commands) { - const struct pa_pdispatch_command *c = pd->command_table+command; - - if (c->proc) - c->proc(pd, command, tag, ts, userdata); - } else - goto finish; - - ret = 0; - -finish: - if (ts) - pa_tagstruct_free(ts); - - return ret; -} - -static void timeout_callback(struct pa_mainloop_api*m, void *id, const struct timeval *tv, void *userdata) { - struct reply_info*r = userdata; - assert (r && r->mainloop_timeout == id && r->pdispatch && r->pdispatch->mainloop == m && r->callback); - - r->callback(r->pdispatch, PA_COMMAND_TIMEOUT, r->tag, NULL, r->userdata); - reply_info_free(r); - - if (r->pdispatch->drain_callback && !pa_pdispatch_is_pending(r->pdispatch)) - r->pdispatch->drain_callback(r->pdispatch, r->pdispatch->drain_userdata); -} - -void pa_pdispatch_register_reply(struct pa_pdispatch *pd, uint32_t tag, int timeout, void (*cb)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata), void *userdata) { - struct reply_info *r; - struct timeval tv; - assert(pd && cb); - - r = malloc(sizeof(struct reply_info)); - assert(r); - r->pdispatch = pd; - r->callback = cb; - r->userdata = userdata; - r->tag = tag; - r->callback_is_running = 0; - - gettimeofday(&tv, NULL); - tv.tv_sec += timeout; - - r->mainloop_timeout = pd->mainloop->source_time(pd->mainloop, &tv, timeout_callback, r); - assert(r->mainloop_timeout); - - r->previous = NULL; - r->next = pd->replies; - if (r->next) - r->next->previous = r; - pd->replies = r; -} - -int pa_pdispatch_is_pending(struct pa_pdispatch *pd) { - assert(pd); - - return !!pd->replies; -} - -void pa_pdispatch_set_drain_callback(struct pa_pdispatch *pd, void (*cb)(struct pa_pdispatch *pd, void *userdata), void *userdata) { - assert(pd); - assert(!cb || pa_pdispatch_is_pending(pd)); - - pd->drain_callback = cb; - pd->drain_userdata = userdata; -} - -void pa_pdispatch_unregister_reply(struct pa_pdispatch *pd, void *userdata) { - struct reply_info *r, *n; - assert(pd); - - for (r = pd->replies; r; r = n) { - n = r->next; - - if (!r->callback_is_running && r->userdata == userdata) /* when this item's callback is currently running it is destroyed anyway in the very near future */ - reply_info_free(r); - } -} diff --git a/src/pdispatch.h b/src/pdispatch.h deleted file mode 100644 index 796c1e03..00000000 --- a/src/pdispatch.h +++ /dev/null @@ -1,52 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include "tagstruct.h" -#include "packet.h" -#include "mainloop-api.h" - -struct pa_pdispatch; - -/* It is safe to destroy the calling pdispatch object from all callbacks */ - -struct pa_pdispatch_command { - void (*proc)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); -}; - -struct pa_pdispatch* pa_pdispatch_new(struct pa_mainloop_api *m, const struct pa_pdispatch_command*table, unsigned entries); -void pa_pdispatch_free(struct pa_pdispatch *pd); - -int pa_pdispatch_run(struct pa_pdispatch *pd, struct pa_packet*p, void *userdata); - -void pa_pdispatch_register_reply(struct pa_pdispatch *pd, uint32_t tag, int timeout, void (*cb)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata), void *userdata); - -int pa_pdispatch_is_pending(struct pa_pdispatch *pd); - -void pa_pdispatch_set_drain_callback(struct pa_pdispatch *pd, void (*cb)(struct pa_pdispatch *pd, void *userdata), void *userdata); - -/* Remove all reply slots with the give userdata parameter */ -void pa_pdispatch_unregister_reply(struct pa_pdispatch *pd, void *userdata); - -#endif diff --git a/src/polypaudio.pa b/src/polypaudio.pa deleted file mode 100755 index 177707ba..00000000 --- a/src/polypaudio.pa +++ /dev/null @@ -1,41 +0,0 @@ -#!./polypaudio -F - -# -# This file is part of polypaudio. -# -# polypaudio is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# polypaudio is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with polypaudio; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - - -# Load audio drivers -load module-alsa-sink -load module-alsa-source device=plughw:1,0 -#load module-oss device="/dev/dsp" -#load module-oss-mmap device="/dev/dsp" - -# Load several protocols -load module-esound-protocol-tcp -load module-simple-protocol-tcp -load module-native-protocol-unix -load module-cli-protocol-unix - -# Load the CLI module -load module-cli - -.nofail - -# Make some devices default -sink_default alsa_output -source_default alsa_input - diff --git a/src/polyplib-def.h b/src/polyplib-def.h deleted file mode 100644 index e43f4b35..00000000 --- a/src/polyplib-def.h +++ /dev/null @@ -1,40 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -enum pa_stream_direction { - PA_STREAM_PLAYBACK, - PA_STREAM_RECORD -}; - -struct pa_buffer_attr { - uint32_t maxlength; - uint32_t tlength; - uint32_t prebuf; - uint32_t minreq; - uint32_t fragsize; -}; - -#endif diff --git a/src/polyplib-error.c b/src/polyplib-error.c deleted file mode 100644 index 10b637c4..00000000 --- a/src/polyplib-error.c +++ /dev/null @@ -1,53 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#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 corrupt", - [PA_ERROR_TIMEOUT] = "Timeout", - [PA_ERROR_AUTHKEY] = "Not authorization key", - [PA_ERROR_INTERNAL] = "Internal error", - [PA_ERROR_CONNECTIONTERMINATED] = "Connection terminated", - [PA_ERROR_KILLED] = "Entity killed", -}; - -const char*pa_strerror(uint32_t error) { - if (error >= PA_ERROR_MAX) - return NULL; - - return errortab[error]; -} diff --git a/src/polyplib-error.h b/src/polyplib-error.h deleted file mode 100644 index cb86864a..00000000 --- a/src/polyplib-error.h +++ /dev/null @@ -1,29 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -const char* pa_strerror(uint32_t error); - -#endif diff --git a/src/polyplib-simple.c b/src/polyplib-simple.c deleted file mode 100644 index 024cb18a..00000000 --- a/src/polyplib-simple.c +++ /dev/null @@ -1,259 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include "polyplib-simple.h" -#include "polyplib.h" -#include "mainloop.h" -#include "native-common.h" -/*#include "polyp-error.h"*/ - -struct pa_simple { - struct pa_mainloop *mainloop; - struct pa_context *context; - struct pa_stream *stream; - enum pa_stream_direction direction; - - int dead, drained; - - void *read_data; - size_t read_index, read_length; -}; - -static void read_callback(struct pa_stream *s, const void*data, size_t length, void *userdata); - -static int check_error(struct pa_simple *p, int *perror) { - assert(p); - - if (pa_context_is_dead(p->context) || (p->stream && pa_stream_is_dead(p->stream))) { - if (perror) - *perror = pa_context_errno(p->context); - return -1; - } - - return 0; -} - -static int iterate(struct pa_simple *p, int block, int *perror) { - assert(p && p->context && p->mainloop); - - if (check_error(p, perror) < 0) - return -1; - - if (!block && !pa_context_is_pending(p->context)) - return 0; - - do { - if (pa_mainloop_iterate(p->mainloop, 1, NULL) < 0) { - if (perror) - *perror = PA_ERROR_INTERNAL; - return -1; - } - - if (check_error(p, perror) < 0) - return -1; - - } while (pa_context_is_pending(p->context)); - - return 0; -} - -struct pa_simple* pa_simple_new( - const char *server, - const char *name, - enum pa_stream_direction dir, - const char *dev, - const char *stream_name, - const struct pa_sample_spec *ss, - const struct pa_buffer_attr *attr, - int *perror) { - - struct pa_simple *p; - int error = PA_ERROR_INTERNAL; - assert(ss); - - p = malloc(sizeof(struct pa_simple)); - assert(p); - p->context = NULL; - p->stream = NULL; - p->mainloop = pa_mainloop_new(); - assert(p->mainloop); - p->dead = 0; - p->direction = dir; - p->read_data = NULL; - p->read_index = p->read_length = 0; - - if (!(p->context = pa_context_new(pa_mainloop_get_api(p->mainloop), name))) - goto fail; - - if (pa_context_connect(p->context, server, NULL, NULL) < 0) { - error = pa_context_errno(p->context); - goto fail; - } - - /* Wait until the context is ready */ - while (!pa_context_is_ready(p->context)) { - if (iterate(p, 1, &error) < 0) - goto fail; - } - - if (!(p->stream = pa_stream_new(p->context, dir, dev, stream_name, ss, attr, NULL, NULL))) - goto fail; - - /* Wait until the stream is ready */ - while (!pa_stream_is_ready(p->stream)) { - if (iterate(p, 1, &error) < 0) - goto fail; - } - - pa_stream_set_read_callback(p->stream, read_callback, p); - - return p; - -fail: - if (perror) - *perror = error; - pa_simple_free(p); - return NULL; -} - -void pa_simple_free(struct pa_simple *s) { - assert(s); - - free(s->read_data); - - if (s->stream) - pa_stream_free(s->stream); - - if (s->context) - pa_context_free(s->context); - - if (s->mainloop) - pa_mainloop_free(s->mainloop); - - free(s); -} - -int pa_simple_write(struct pa_simple *p, const void*data, size_t length, int *perror) { - assert(p && data && p->direction == PA_STREAM_PLAYBACK); - - while (length > 0) { - size_t l; - - while (!(l = pa_stream_writable_size(p->stream))) - if (iterate(p, 1, perror) < 0) - return -1; - - if (l > length) - l = length; - - pa_stream_write(p->stream, data, l); - data += l; - length -= l; - } - - /* Make sure that no data is pending for write */ - if (iterate(p, 0, perror) < 0) - return -1; - - return 0; -} - -static void read_callback(struct pa_stream *s, const void*data, size_t length, void *userdata) { - struct pa_simple *p = userdata; - assert(s && data && length && p); - - if (p->read_data) { - fprintf(stderr, __FILE__": Buffer overflow, dropping incoming memory blocks.\n"); - free(p->read_data); - } - - p->read_data = malloc(p->read_length = length); - assert(p->read_data); - memcpy(p->read_data, data, length); - p->read_index = 0; -} - -int pa_simple_read(struct pa_simple *p, void*data, size_t length, int *perror) { - assert(p && data && p->direction == PA_STREAM_RECORD); - - while (length > 0) { - if (p->read_data) { - size_t l = length; - - if (p->read_length <= l) - l = p->read_length; - - memcpy(data, p->read_data+p->read_index, l); - - data += l; - length -= l; - - p->read_index += l; - p->read_length -= l; - - if (!p->read_length) { - free(p->read_data); - p->read_data = NULL; - p->read_index = 0; - } - - if (!length) - return 0; - - assert(!p->read_data); - } - - if (iterate(p, 1, perror) < 0) - return -1; - } - - return 0; -} - -static void drain_complete(struct pa_stream *s, void *userdata) { - struct pa_simple *p = userdata; - assert(s && p); - p->drained = 1; -} - -int pa_simple_drain(struct pa_simple *p, int *perror) { - assert(p && p->direction == PA_STREAM_PLAYBACK); - p->drained = 0; - pa_stream_drain(p->stream, drain_complete, p); - - while (!p->drained) { - if (iterate(p, 1, perror) < 0) { - pa_stream_drain(p->stream, NULL, NULL); - return -1; - } - } - - return 0; -} diff --git a/src/polyplib-simple.h b/src/polyplib-simple.h deleted file mode 100644 index 0544410c..00000000 --- a/src/polyplib-simple.h +++ /dev/null @@ -1,49 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -#include "sample.h" -#include "polyplib-def.h" - -struct pa_simple; - -struct pa_simple* pa_simple_new( - const char *server, - const char *name, - enum pa_stream_direction dir, - const char *dev, - const char *stream_name, - const struct pa_sample_spec *ss, - const struct pa_buffer_attr *attr, - int *error); - -void pa_simple_free(struct pa_simple *s); - -int pa_simple_write(struct pa_simple *s, const void*data, size_t length, int *error); -int pa_simple_drain(struct pa_simple *s, int *error); - -int pa_simple_read(struct pa_simple *s, void*data, size_t length, int *error); - -#endif diff --git a/src/polyplib.c b/src/polyplib.c deleted file mode 100644 index ea5003b4..00000000 --- a/src/polyplib.c +++ /dev/null @@ -1,973 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "polyplib.h" -#include "native-common.h" -#include "pdispatch.h" -#include "pstream.h" -#include "dynarray.h" -#include "socket-client.h" -#include "pstream-util.h" -#include "authkey.h" -#include "util.h" - -#define DEFAULT_MAXLENGTH 204800 -#define DEFAULT_TLENGTH 10240 -#define DEFAULT_PREBUF 4096 -#define DEFAULT_MINREQ 1024 -#define DEFAULT_FRAGSIZE 1024 - -#define DEFAULT_TIMEOUT (5*60) -#define DEFAULT_SERVER "/tmp/polypaudio/native" -#define DEFAULT_PORT "4713" - -struct pa_context { - char *name; - struct pa_mainloop_api* mainloop; - struct pa_socket_client *client; - struct pa_pstream *pstream; - struct pa_pdispatch *pdispatch; - struct pa_dynarray *record_streams, *playback_streams; - struct pa_stream *first_stream; - uint32_t ctag; - uint32_t error; - enum { - CONTEXT_UNCONNECTED, - CONTEXT_CONNECTING, - CONTEXT_AUTHORIZING, - CONTEXT_SETTING_NAME, - CONTEXT_READY, - CONTEXT_DEAD - } state; - - void (*connect_complete_callback)(struct pa_context*c, int success, void *userdata); - void *connect_complete_userdata; - - void (*drain_complete_callback)(struct pa_context*c, void *userdata); - void *drain_complete_userdata; - - void (*die_callback)(struct pa_context*c, void *userdata); - void *die_userdata; - - void (*stat_callback)(struct pa_context*c, uint32_t count, uint32_t total, void *userdata); - void *stat_userdata; - - uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH]; -}; - -struct pa_stream { - struct pa_context *context; - struct pa_stream *next, *previous; - - char *name; - struct pa_buffer_attr buffer_attr; - struct pa_sample_spec sample_spec; - uint32_t device_index; - uint32_t channel; - int channel_valid; - enum pa_stream_direction direction; - - enum { STREAM_LOOKING_UP, STREAM_CREATING, STREAM_READY, STREAM_DEAD} state; - uint32_t requested_bytes; - - void (*read_callback)(struct pa_stream *p, const void*data, size_t length, void *userdata); - void *read_userdata; - - void (*write_callback)(struct pa_stream *p, size_t length, void *userdata); - void *write_userdata; - - void (*create_complete_callback)(struct pa_stream *s, int success, void *userdata); - void *create_complete_userdata; - - void (*drain_complete_callback)(struct pa_stream *s, void *userdata); - void *drain_complete_userdata; - - void (*die_callback)(struct pa_stream*c, void *userdata); - void *die_userdata; - - void (*get_latency_callback)(struct pa_stream*c, uint32_t latency, void *userdata); - void *get_latency_userdata; -}; - -static void command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); -static void command_playback_stream_killed(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); - -static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = { - [PA_COMMAND_ERROR] = { NULL }, - [PA_COMMAND_REPLY] = { NULL }, - [PA_COMMAND_CREATE_PLAYBACK_STREAM] = { NULL }, - [PA_COMMAND_DELETE_PLAYBACK_STREAM] = { NULL }, - [PA_COMMAND_CREATE_RECORD_STREAM] = { NULL }, - [PA_COMMAND_DELETE_RECORD_STREAM] = { NULL }, - [PA_COMMAND_EXIT] = { NULL }, - [PA_COMMAND_REQUEST] = { command_request }, - [PA_COMMAND_PLAYBACK_STREAM_KILLED] = { command_playback_stream_killed }, - [PA_COMMAND_RECORD_STREAM_KILLED] = { command_playback_stream_killed }, -}; - -struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *name) { - struct pa_context *c; - assert(mainloop && name); - - c = malloc(sizeof(struct pa_context)); - assert(c); - c->name = strdup(name); - c->mainloop = mainloop; - c->client = NULL; - c->pstream = NULL; - c->pdispatch = NULL; - c->playback_streams = pa_dynarray_new(); - assert(c->playback_streams); - c->record_streams = pa_dynarray_new(); - assert(c->record_streams); - c->first_stream = NULL; - c->error = PA_ERROR_OK; - c->state = CONTEXT_UNCONNECTED; - c->ctag = 0; - - c->connect_complete_callback = NULL; - c->connect_complete_userdata = NULL; - - c->drain_complete_callback = NULL; - c->drain_complete_userdata = NULL; - - c->die_callback = NULL; - c->die_userdata = NULL; - - c->stat_callback = NULL; - c->stat_userdata = NULL; - - pa_check_for_sigpipe(); - return c; -} - -void pa_context_free(struct pa_context *c) { - assert(c); - - while (c->first_stream) - pa_stream_free(c->first_stream); - - if (c->client) - pa_socket_client_free(c->client); - if (c->pdispatch) - pa_pdispatch_free(c->pdispatch); - if (c->pstream) - pa_pstream_free(c->pstream); - if (c->record_streams) - pa_dynarray_free(c->record_streams, NULL, NULL); - if (c->playback_streams) - pa_dynarray_free(c->playback_streams, NULL, NULL); - - free(c->name); - free(c); -} - -static void stream_dead(struct pa_stream *s) { - assert(s); - - if (s->state == STREAM_DEAD) - return; - - if (s->state == STREAM_READY) { - s->state = STREAM_DEAD; - if (s->die_callback) - s->die_callback(s, s->die_userdata); - } else - s->state = STREAM_DEAD; -} - -static void context_dead(struct pa_context *c) { - struct pa_stream *s; - assert(c); - - if (c->state == CONTEXT_DEAD) - return; - - if (c->pdispatch) - pa_pdispatch_free(c->pdispatch); - c->pdispatch = NULL; - - if (c->pstream) - pa_pstream_free(c->pstream); - c->pstream = NULL; - - if (c->client) - pa_socket_client_free(c->client); - c->client = NULL; - - for (s = c->first_stream; s; s = s->next) - stream_dead(s); - - if (c->state == CONTEXT_READY) { - c->state = CONTEXT_DEAD; - if (c->die_callback) - c->die_callback(c, c->die_userdata); - } else - c->state = CONTEXT_DEAD; -} - -static void pstream_die_callback(struct pa_pstream *p, void *userdata) { - struct pa_context *c = userdata; - assert(p && c); - c->error = PA_ERROR_CONNECTIONTERMINATED; - context_dead(c); -} - -static void pstream_packet_callback(struct pa_pstream *p, struct pa_packet *packet, void *userdata) { - struct pa_context *c = userdata; - assert(p && packet && c); - - if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) { - fprintf(stderr, "polyp.c: invalid packet.\n"); - c->error = PA_ERROR_PROTOCOL; - context_dead(c); - } -} - -static void pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata) { - struct pa_context *c = userdata; - struct pa_stream *s; - assert(p && chunk && c && chunk->memblock && chunk->memblock->data); - - if (!(s = pa_dynarray_get(c->record_streams, channel))) - return; - - if (s->read_callback) - s->read_callback(s, chunk->memblock->data + chunk->index, chunk->length, s->read_userdata); -} - -static int handle_error(struct pa_context *c, uint32_t command, struct pa_tagstruct *t) { - assert(c && t); - - if (command == PA_COMMAND_ERROR) { - if (pa_tagstruct_getu32(t, &c->error) < 0) { - c->error = PA_ERROR_PROTOCOL; - return -1; - } - - return 0; - } - - c->error = (command == PA_COMMAND_TIMEOUT) ? PA_ERROR_TIMEOUT : PA_ERROR_INTERNAL; - return -1; -} - -static void setup_complete_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct pa_context *c = userdata; - assert(pd && c && (c->state == CONTEXT_AUTHORIZING || c->state == CONTEXT_SETTING_NAME)); - - if (command != PA_COMMAND_REPLY) { - handle_error(c, command, t); - context_dead(c); - - if (c->connect_complete_callback) - c->connect_complete_callback(c, 0, c->connect_complete_userdata); - - return; - } - - if (c->state == CONTEXT_AUTHORIZING) { - struct pa_tagstruct *t; - c->state = CONTEXT_SETTING_NAME; - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_SET_NAME); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_puts(t, c->name); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c); - } else { - assert(c->state == CONTEXT_SETTING_NAME); - - c->state = CONTEXT_READY; - - if (c->connect_complete_callback) - c->connect_complete_callback(c, 1, c->connect_complete_userdata); - } - - return; -} - -static void on_connection(struct pa_socket_client *client, struct pa_iochannel*io, void *userdata) { - struct pa_context *c = userdata; - struct pa_tagstruct *t; - uint32_t tag; - assert(client && c && c->state == CONTEXT_CONNECTING); - - pa_socket_client_free(client); - c->client = NULL; - - if (!io) { - c->error = PA_ERROR_CONNECTIONREFUSED; - context_dead(c); - - if (c->connect_complete_callback) - c->connect_complete_callback(c, 0, c->connect_complete_userdata); - - return; - } - - c->pstream = pa_pstream_new(c->mainloop, io); - assert(c->pstream); - pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c); - pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c); - pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c); - - c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX); - assert(c->pdispatch); - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_AUTH); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_tagstruct_put_arbitrary(t, c->auth_cookie, sizeof(c->auth_cookie)); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c); - c->state = CONTEXT_AUTHORIZING; -} - -static struct sockaddr *resolve_server(const char *server, size_t *len) { - struct sockaddr *sa; - struct addrinfo hints, *result = NULL; - char *port; - assert(server && len); - - if ((port = strrchr(server, ':'))) - port++; - if (!port) - port = DEFAULT_PORT; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; - - if (getaddrinfo(server, port, &hints, &result) != 0) - return NULL; - assert(result); - - sa = malloc(*len = result->ai_addrlen); - assert(sa); - memcpy(sa, result->ai_addr, *len); - - freeaddrinfo(result); - - return sa; - -} - -int pa_context_connect(struct pa_context *c, const char *server, void (*complete) (struct pa_context*c, int success, void *userdata), void *userdata) { - assert(c && c->state == CONTEXT_UNCONNECTED); - - if (pa_authkey_load_from_home(PA_NATIVE_COOKIE_FILE, c->auth_cookie, sizeof(c->auth_cookie)) < 0) { - c->error = PA_ERROR_AUTHKEY; - return -1; - } - - if (!server) - if (!(server = getenv("POLYP_SERVER"))) - server = DEFAULT_SERVER; - - assert(!c->client); - - if (*server == '/') { - if (!(c->client = pa_socket_client_new_unix(c->mainloop, server))) { - c->error = PA_ERROR_CONNECTIONREFUSED; - return -1; - } - } else { - struct sockaddr* sa; - size_t sa_len; - - if (!(sa = resolve_server(server, &sa_len))) { - c->error = PA_ERROR_INVALIDSERVER; - return -1; - } - - c->client = pa_socket_client_new_sockaddr(c->mainloop, sa, sa_len); - free(sa); - - if (!c->client) { - c->error = PA_ERROR_CONNECTIONREFUSED; - return -1; - } - } - - c->connect_complete_callback = complete; - c->connect_complete_userdata = userdata; - - pa_socket_client_set_callback(c->client, on_connection, c); - c->state = CONTEXT_CONNECTING; - - return 0; -} - -int pa_context_is_dead(struct pa_context *c) { - assert(c); - return c->state == CONTEXT_DEAD; -} - -int pa_context_is_ready(struct pa_context *c) { - assert(c); - return c->state == CONTEXT_READY; -} - -int pa_context_errno(struct pa_context *c) { - assert(c); - return c->error; -} - -void pa_context_set_die_callback(struct pa_context *c, void (*cb)(struct pa_context *c, void *userdata), void *userdata) { - assert(c); - c->die_callback = cb; - c->die_userdata = userdata; -} - -static void command_playback_stream_killed(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct pa_context *c = userdata; - struct pa_stream *s; - uint32_t channel; - assert(pd && (command == PA_COMMAND_PLAYBACK_STREAM_KILLED || command == PA_COMMAND_RECORD_STREAM_KILLED) && t && c); - - if (pa_tagstruct_getu32(t, &channel) < 0 || - !pa_tagstruct_eof(t)) { - c->error = PA_ERROR_PROTOCOL; - context_dead(c); - return; - } - - if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel))) - return; - - c->error = PA_ERROR_KILLED; - stream_dead(s); -} - -static void command_request(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct pa_stream *s; - struct pa_context *c = userdata; - uint32_t bytes, channel; - assert(pd && command == PA_COMMAND_REQUEST && t && c); - - if (pa_tagstruct_getu32(t, &channel) < 0 || - pa_tagstruct_getu32(t, &bytes) < 0 || - !pa_tagstruct_eof(t)) { - c->error = PA_ERROR_PROTOCOL; - context_dead(c); - return; - } - - if (!(s = pa_dynarray_get(c->playback_streams, channel))) - return; - - if (s->state != STREAM_READY) - return; - - s->requested_bytes += bytes; - - if (s->requested_bytes && s->write_callback) - s->write_callback(s, s->requested_bytes, s->write_userdata); -} - -static void create_stream_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct pa_stream *s = userdata; - assert(pd && s && s->state == STREAM_CREATING); - - if (command != PA_COMMAND_REPLY) { - if (handle_error(s->context, command, t) < 0) { - context_dead(s->context); - return; - } - - stream_dead(s); - if (s->create_complete_callback) - s->create_complete_callback(s, 0, s->create_complete_userdata); - - return; - } - - if (pa_tagstruct_getu32(t, &s->channel) < 0 || - pa_tagstruct_getu32(t, &s->device_index) < 0 || - !pa_tagstruct_eof(t)) { - s->context->error = PA_ERROR_PROTOCOL; - context_dead(s->context); - return; - } - - s->channel_valid = 1; - pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, s); - - s->state = STREAM_READY; - if (s->create_complete_callback) - s->create_complete_callback(s, 1, s->create_complete_userdata); -} - -static void create_stream(struct pa_stream *s, uint32_t tdev_index) { - struct pa_tagstruct *t; - uint32_t tag; - assert(s); - - s->state = STREAM_CREATING; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - - pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - pa_tagstruct_puts(t, s->name); - pa_tagstruct_put_sample_spec(t, &s->sample_spec); - pa_tagstruct_putu32(t, tdev_index); - pa_tagstruct_putu32(t, s->buffer_attr.maxlength); - if (s->direction == PA_STREAM_PLAYBACK) { - pa_tagstruct_putu32(t, s->buffer_attr.tlength); - pa_tagstruct_putu32(t, s->buffer_attr.prebuf); - pa_tagstruct_putu32(t, s->buffer_attr.minreq); - } else - pa_tagstruct_putu32(t, s->buffer_attr.fragsize); - - pa_pstream_send_tagstruct(s->context->pstream, t); - pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, s); -} - -static void lookup_device_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct pa_stream *s = userdata; - uint32_t tdev; - assert(pd && s && s->state == STREAM_LOOKING_UP); - - if (command != PA_COMMAND_REPLY) { - if (handle_error(s->context, command, t) < 0) { - context_dead(s->context); - return; - } - - stream_dead(s); - if (s->create_complete_callback) - s->create_complete_callback(s, 0, s->create_complete_userdata); - return; - } - - if (pa_tagstruct_getu32(t, &tdev) < 0 || - !pa_tagstruct_eof(t)) { - s->context->error = PA_ERROR_PROTOCOL; - context_dead(s->context); - return; - } - - create_stream(s, tdev); -} - -static void lookup_device(struct pa_stream *s, const char *tdev) { - struct pa_tagstruct *t; - uint32_t tag; - assert(s); - - s->state = STREAM_LOOKING_UP; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - - pa_tagstruct_putu32(t, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_LOOKUP_SINK : PA_COMMAND_LOOKUP_SOURCE); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - pa_tagstruct_puts(t, tdev); - - pa_pstream_send_tagstruct(s->context->pstream, t); - pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, lookup_device_callback, s); -} - -struct pa_stream* pa_stream_new( - struct pa_context *c, - enum pa_stream_direction dir, - const char *dev, - const char *name, - const struct pa_sample_spec *ss, - const struct pa_buffer_attr *attr, - void (*complete) (struct pa_stream*s, int success, void *userdata), - void *userdata) { - - struct pa_stream *s; - - assert(c && name && ss && c->state == CONTEXT_READY); - - s = malloc(sizeof(struct pa_stream)); - assert(s); - s->context = c; - - s->read_callback = NULL; - s->read_userdata = NULL; - s->write_callback = NULL; - s->write_userdata = NULL; - s->die_callback = NULL; - s->die_userdata = NULL; - s->create_complete_callback = complete; - s->create_complete_userdata = NULL; - s->get_latency_callback = NULL; - s->get_latency_userdata = NULL; - - s->name = strdup(name); - s->state = STREAM_CREATING; - s->requested_bytes = 0; - s->channel = 0; - s->channel_valid = 0; - s->device_index = (uint32_t) -1; - s->direction = dir; - s->sample_spec = *ss; - if (attr) - s->buffer_attr = *attr; - else { - s->buffer_attr.maxlength = DEFAULT_MAXLENGTH; - s->buffer_attr.tlength = DEFAULT_TLENGTH; - s->buffer_attr.prebuf = DEFAULT_PREBUF; - s->buffer_attr.minreq = DEFAULT_MINREQ; - s->buffer_attr.fragsize = DEFAULT_FRAGSIZE; - } - - s->next = c->first_stream; - if (s->next) - s->next->previous = s; - s->previous = NULL; - c->first_stream = s; - - if (dev) - lookup_device(s, dev); - else - create_stream(s, (uint32_t) -1); - - return s; -} - -void pa_stream_free(struct pa_stream *s) { - assert(s && s->context); - - if (s->context->pdispatch) - pa_pdispatch_unregister_reply(s->context->pdispatch, s); - - free(s->name); - - if (s->channel_valid && s->context->state == CONTEXT_READY) { - struct pa_tagstruct *t = pa_tagstruct_new(NULL, 0); - assert(t); - - pa_tagstruct_putu32(t, PA_COMMAND_DELETE_PLAYBACK_STREAM); - pa_tagstruct_putu32(t, s->context->ctag++); - pa_tagstruct_putu32(t, s->channel); - pa_pstream_send_tagstruct(s->context->pstream, t); - } - - if (s->channel_valid) - pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL); - - if (s->next) - s->next->previous = s->previous; - if (s->previous) - s->previous->next = s->next; - else - s->context->first_stream = s->next; - - free(s); -} - -void pa_stream_set_write_callback(struct pa_stream *s, void (*cb)(struct pa_stream *p, size_t length, void *userdata), void *userdata) { - assert(s && cb); - s->write_callback = cb; - s->write_userdata = userdata; -} - -void pa_stream_write(struct pa_stream *s, const void *data, size_t length) { - struct pa_memchunk chunk; - assert(s && s->context && data && length && s->state == STREAM_READY); - - chunk.memblock = pa_memblock_new(length); - assert(chunk.memblock && chunk.memblock->data); - memcpy(chunk.memblock->data, data, length); - chunk.index = 0; - chunk.length = length; - - pa_pstream_send_memblock(s->context->pstream, s->channel, 0, &chunk); - pa_memblock_unref(chunk.memblock); - - /*fprintf(stderr, "Sent %u bytes\n", length);*/ - - if (length < s->requested_bytes) - s->requested_bytes -= length; - else - s->requested_bytes = 0; -} - -size_t pa_stream_writable_size(struct pa_stream *s) { - assert(s && s->state == STREAM_READY); - return s->requested_bytes; -} - -void pa_stream_set_read_callback(struct pa_stream *s, void (*cb)(struct pa_stream *p, const void*data, size_t length, void *userdata), void *userdata) { - assert(s && cb); - s->read_callback = cb; - s->read_userdata = userdata; -} - -int pa_stream_is_dead(struct pa_stream *s) { - return s->state == STREAM_DEAD; -} - -int pa_stream_is_ready(struct pa_stream*s) { - return s->state == STREAM_READY; -} - -void pa_stream_set_die_callback(struct pa_stream *s, void (*cb)(struct pa_stream *s, void *userdata), void *userdata) { - assert(s); - s->die_callback = cb; - s->die_userdata = userdata; -} - -int pa_context_is_pending(struct pa_context *c) { - assert(c); - - if (c->state != CONTEXT_READY) - return 0; - - return pa_pstream_is_pending(c->pstream) || pa_pdispatch_is_pending(c->pdispatch); -} - -struct pa_context* pa_stream_get_context(struct pa_stream *p) { - assert(p); - return p->context; -} - -static void set_dispatch_callbacks(struct pa_context *c); - -static void pdispatch_drain_callback(struct pa_pdispatch*pd, void *userdata) { - set_dispatch_callbacks(userdata); -} - -static void pstream_drain_callback(struct pa_pstream *s, void *userdata) { - set_dispatch_callbacks(userdata); -} - -static void set_dispatch_callbacks(struct pa_context *c) { - assert(c && c->state == CONTEXT_READY); - - pa_pstream_set_drain_callback(c->pstream, NULL, NULL); - pa_pdispatch_set_drain_callback(c->pdispatch, NULL, NULL); - - if (pa_pdispatch_is_pending(c->pdispatch)) { - pa_pdispatch_set_drain_callback(c->pdispatch, pdispatch_drain_callback, c); - return; - } - - if (pa_pstream_is_pending(c->pstream)) { - pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c); - return; - } - - assert(c->drain_complete_callback); - c->drain_complete_callback(c, c->drain_complete_userdata); -} - -int pa_context_drain( - struct pa_context *c, - void (*complete) (struct pa_context*c, void *userdata), - void *userdata) { - - assert(c && c->state == CONTEXT_READY); - - if (complete == NULL) { - c->drain_complete_callback = NULL; - pa_pstream_set_drain_callback(c->pstream, NULL, NULL); - pa_pdispatch_set_drain_callback(c->pdispatch, NULL, NULL); - return 0; - } - - if (!pa_context_is_pending(c)) - return -1; - - c->drain_complete_callback = complete; - c->drain_complete_userdata = userdata; - - set_dispatch_callbacks(c); - - return 0; -} - -static void stream_drain_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct pa_stream *s = userdata; - assert(pd && s); - - if (command != PA_COMMAND_REPLY) { - if (handle_error(s->context, command, t) < 0) { - context_dead(s->context); - return; - } - - stream_dead(s); - return; - } - - if (s->state != STREAM_READY) - return; - - if (!pa_tagstruct_eof(t)) { - s->context->error = PA_ERROR_PROTOCOL; - context_dead(s->context); - return; - } - - if (s->drain_complete_callback) { - void (*temp) (struct pa_stream*s, void *userdata) = s->drain_complete_callback; - s->drain_complete_callback = NULL; - temp(s, s->drain_complete_userdata); - } -} - - -void pa_stream_drain(struct pa_stream *s, void (*complete) (struct pa_stream*s, void *userdata), void *userdata) { - struct pa_tagstruct *t; - uint32_t tag; - assert(s && s->state == STREAM_READY); - - if (!complete) { - s->drain_complete_callback = NULL; - return; - } - - s->drain_complete_callback = complete; - s->drain_complete_userdata = userdata; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - - pa_tagstruct_putu32(t, PA_COMMAND_DRAIN_PLAYBACK_STREAM); - pa_tagstruct_putu32(t, tag = s->context->ctag++); - pa_tagstruct_putu32(t, s->channel); - pa_pstream_send_tagstruct(s->context->pstream, t); - pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_drain_callback, s); -} - -void pa_context_exit(struct pa_context *c) { - struct pa_tagstruct *t; - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_EXIT); - pa_tagstruct_putu32(t, c->ctag++); - pa_pstream_send_tagstruct(c->pstream, t); -} - -static void context_stat_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct pa_context *c = userdata; - uint32_t total, count; - assert(pd && c); - - if (command != PA_COMMAND_REPLY) { - if (handle_error(c, command, t) < 0) { - context_dead(c); - return; - } - - if (c->stat_callback) - c->stat_callback(c, (uint32_t) -1, (uint32_t) -1, c->stat_userdata); - return; - } - - if (pa_tagstruct_getu32(t, &count) < 0 || - pa_tagstruct_getu32(t, &total) < 0 || - !pa_tagstruct_eof(t)) { - c->error = PA_ERROR_PROTOCOL; - context_dead(c); - return; - } - - if (c->stat_callback) - c->stat_callback(c, count, total, c->stat_userdata); -} - -void pa_context_stat(struct pa_context *c, void (*cb)(struct pa_context *c, uint32_t count, uint32_t total, void *userdata), void *userdata) { - uint32_t tag; - struct pa_tagstruct *t; - - c->stat_callback = cb; - c->stat_userdata = userdata; - - if (cb == NULL) - return; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_STAT); - pa_tagstruct_putu32(t, tag = c->ctag++); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_stat_callback, c); -} - -static void stream_get_latency_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct pa_stream *s = userdata; - uint32_t latency; - assert(pd && s); - - if (command != PA_COMMAND_REPLY) { - if (handle_error(s->context, command, t) < 0) { - context_dead(s->context); - return; - } - - if (s->get_latency_callback) - s->get_latency_callback(s, (uint32_t) -1, s->get_latency_userdata); - return; - } - - if (pa_tagstruct_getu32(t, &latency) < 0 || - !pa_tagstruct_eof(t)) { - s->context->error = PA_ERROR_PROTOCOL; - context_dead(s->context); - return; - } - - if (s->get_latency_callback) - s->get_latency_callback(s, latency, s->get_latency_userdata); -} - -void pa_stream_get_latency(struct pa_stream *p, void (*cb)(struct pa_stream *p, uint32_t latency, void *userdata), void *userdata) { - uint32_t tag; - struct pa_tagstruct *t; - - p->get_latency_callback = cb; - p->get_latency_userdata = userdata; - - if (cb == NULL) - return; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY); - pa_tagstruct_putu32(t, tag = p->context->ctag++); - pa_tagstruct_putu32(t, p->channel); - pa_pstream_send_tagstruct(p->context->pstream, t); - pa_pdispatch_register_reply(p->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, p); -} diff --git a/src/polyplib.h b/src/polyplib.h deleted file mode 100644 index 440b9658..00000000 --- a/src/polyplib.h +++ /dev/null @@ -1,94 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -#include "sample.h" -#include "polyplib-def.h" -#include "mainloop-api.h" - -struct pa_context; - -struct pa_context *pa_context_new(struct pa_mainloop_api *mainloop, const char *name); - -int pa_context_connect( - struct pa_context *c, - const char *server, - void (*complete) (struct pa_context*c, int success, void *userdata), - void *userdata); - -int pa_context_drain( - struct pa_context *c, - void (*complete) (struct pa_context*c, void *userdata), - void *userdata); - -void pa_context_free(struct pa_context *c); - -void pa_context_set_die_callback(struct pa_context *c, void (*cb)(struct pa_context *c, void *userdata), void *userdata); - -int pa_context_is_dead(struct pa_context *c); -int pa_context_is_ready(struct pa_context *c); -int pa_context_errno(struct pa_context *c); - -int pa_context_is_pending(struct pa_context *c); - -void pa_context_exit(struct pa_context *c); -void pa_context_stat(struct pa_context *c, void (*cb)(struct pa_context *c, uint32_t count, uint32_t total, void *userdata), void *userdata); - -struct pa_stream; - -struct pa_stream* pa_stream_new( - struct pa_context *c, - enum pa_stream_direction dir, - const char *dev, - const char *name, - const struct pa_sample_spec *ss, - const struct pa_buffer_attr *attr, - void (*complete) (struct pa_stream*s, int success, void *userdata), - void *userdata); - -void pa_stream_free(struct pa_stream *p); - -void pa_stream_drain( - struct pa_stream *s, - void (*complete) (struct pa_stream*s, void *userdata), - void *userdata); - - -void pa_stream_set_die_callback(struct pa_stream *s, void (*cb)(struct pa_stream *s, void *userdata), void *userdata); - -void pa_stream_set_write_callback(struct pa_stream *p, void (*cb)(struct pa_stream *p, size_t length, void *userdata), void *userdata); -void pa_stream_write(struct pa_stream *p, const void *data, size_t length); -size_t pa_stream_writable_size(struct pa_stream *p); - -void pa_stream_set_read_callback(struct pa_stream *p, void (*cb)(struct pa_stream *p, const void*data, size_t length, void *userdata), void *userdata); - -int pa_stream_is_dead(struct pa_stream *p); -int pa_stream_is_ready(struct pa_stream*p); - -void pa_stream_get_latency(struct pa_stream *p, void (*cb)(struct pa_stream *p, uint32_t latency, void *userdata), void *userdata); - -struct pa_context* pa_stream_get_context(struct pa_stream *p); - -#endif diff --git a/src/protocol-cli.c b/src/protocol-cli.c deleted file mode 100644 index d6e69b54..00000000 --- a/src/protocol-cli.c +++ /dev/null @@ -1,85 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include "protocol-cli.h" -#include "cli.h" - -struct pa_protocol_cli { - struct pa_module *module; - struct pa_core *core; - struct pa_socket_server*server; - struct pa_idxset *connections; -}; - -static void cli_eof_cb(struct pa_cli*c, void*userdata) { - struct pa_protocol_cli *p = userdata; - assert(p); - pa_idxset_remove_by_data(p->connections, c, NULL); - pa_cli_free(c); -} - -static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) { - struct pa_protocol_cli *p = userdata; - struct pa_cli *c; - assert(s && io && p); - - c = pa_cli_new(p->core, io, p->module); - assert(c); - pa_cli_set_eof_callback(c, cli_eof_cb, p); - - pa_idxset_put(p->connections, c, NULL); -} - -struct pa_protocol_cli* pa_protocol_cli_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) { - struct pa_protocol_cli* p; - assert(core && server); - - p = malloc(sizeof(struct pa_protocol_cli)); - assert(p); - p->module = m; - p->core = core; - p->server = server; - p->connections = pa_idxset_new(NULL, NULL); - - pa_socket_server_set_callback(p->server, on_connection, p); - - return p; -} - -static void free_connection(void *p, void *userdata) { - assert(p); - pa_cli_free(p); -} - -void pa_protocol_cli_free(struct pa_protocol_cli *p) { - assert(p); - - pa_idxset_free(p->connections, free_connection, NULL); - pa_socket_server_free(p->server); - free(p); -} diff --git a/src/protocol-cli.h b/src/protocol-cli.h deleted file mode 100644 index 7ad2db75..00000000 --- a/src/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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "core.h" -#include "socket-server.h" -#include "module.h" -#include "modargs.h" - -struct pa_protocol_cli; - -struct pa_protocol_cli* pa_protocol_cli_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma); -void pa_protocol_cli_free(struct pa_protocol_cli *n); - -#endif diff --git a/src/protocol-esound.c b/src/protocol-esound.c deleted file mode 100644 index 8a7c4bcb..00000000 --- a/src/protocol-esound.c +++ /dev/null @@ -1,847 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#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 "authkey.h" - -#define DEFAULT_COOKIE_FILE ".esd_auth" - -#define PLAYBACK_BUFFER_SECONDS (.5) -#define PLAYBACK_BUFFER_FRAGMENTS (10) -#define RECORD_BUFFER_SECONDS (5) -#define RECORD_BUFFER_FRAGMENTS (100) - -/* This is heavily based on esound's code */ - -struct connection { - uint32_t index; - struct pa_protocol_esound *protocol; - struct pa_iochannel *io; - struct pa_client *client; - int authorized, swap_byte_order; - void *write_data; - size_t write_data_alloc, write_data_index, write_data_length; - void *read_data; - size_t read_data_alloc, read_data_length; - esd_proto_t request; - esd_client_state_t state; - struct pa_sink_input *sink_input; - struct pa_source_output *source_output; - struct pa_memblockq *input_memblockq, *output_memblockq; - void *fixed_source; - struct { - struct pa_memblock *current_memblock; - size_t memblock_index, fragment_size; - } playback; -}; - -struct pa_protocol_esound { - int public; - struct pa_module *module; - struct pa_core *core; - struct pa_socket_server *server; - struct pa_idxset *connections; - uint32_t sink_index, source_index; - unsigned n_player; - uint8_t esd_key[ESD_KEY_LEN]; -}; - -typedef struct proto_handler { - size_t data_length; - int (*proc)(struct connection *c, esd_proto_t request, const void *data, size_t length); - const char *description; -} esd_proto_handler_info_t; - -static void sink_input_drop_cb(struct pa_sink_input *i, size_t length); -static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk); -static void sink_input_kill_cb(struct pa_sink_input *i); -static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i); - -static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk); -static void source_output_kill_cb(struct pa_source_output *o); - -static int esd_proto_connect(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_stream_play(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_get_latency(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_server_info(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length); -static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const void *data, size_t length); - -/* the big map of protocol handler info */ -static struct proto_handler proto_map[ESD_PROTO_MAX] = { - { ESD_KEY_LEN + sizeof(int), esd_proto_connect, "connect" }, - { ESD_KEY_LEN + sizeof(int), NULL, "lock" }, - { ESD_KEY_LEN + sizeof(int), NULL, "unlock" }, - - { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_play, "stream play" }, - { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream rec" }, - { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream mon" }, - - { ESD_NAME_MAX + 3 * sizeof(int), NULL, "sample cache" }, - { sizeof(int), NULL, "sample free" }, - { sizeof(int), NULL, "sample play" }, - { sizeof(int), NULL, "sample loop" }, - { sizeof(int), NULL, "sample stop" }, - { -1, NULL, "TODO: sample kill" }, - - { ESD_KEY_LEN + sizeof(int), NULL, "standby" }, - { ESD_KEY_LEN + sizeof(int), NULL, "resume" }, - - { ESD_NAME_MAX, NULL, "sample getid" }, - { ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" }, - - { sizeof(int), esd_proto_server_info, "server info" }, - { sizeof(int), esd_proto_all_info, "all info" }, - { -1, NULL, "TODO: subscribe" }, - { -1, NULL, "TODO: unsubscribe" }, - - { 3 * sizeof(int), esd_proto_stream_pan, "stream pan"}, - { 3 * sizeof(int), NULL, "sample pan" }, - - { sizeof(int), NULL, "standby mode" }, - { 0, esd_proto_get_latency, "get latency" } -}; - - -static void connection_free(struct connection *c) { - assert(c); - pa_idxset_remove_by_data(c->protocol->connections, c, NULL); - - if (c->state == ESD_STREAMING_DATA) - c->protocol->n_player--; - - pa_client_free(c->client); - - if (c->sink_input) - pa_sink_input_free(c->sink_input); - if (c->source_output) - pa_source_output_free(c->source_output); - if (c->input_memblockq) - pa_memblockq_free(c->input_memblockq); - if (c->output_memblockq) - pa_memblockq_free(c->output_memblockq); - - if (c->playback.current_memblock) - pa_memblock_unref(c->playback.current_memblock); - - free(c->read_data); - free(c->write_data); - - pa_iochannel_free(c->io); - - if (c->fixed_source) - c->protocol->core->mainloop->cancel_fixed(c->protocol->core->mainloop, c->fixed_source); - - free(c); -} - -static struct pa_sink* get_output_sink(struct pa_protocol_esound *p) { - struct pa_sink *s; - assert(p); - - if (!(s = pa_idxset_get_by_index(p->core->sinks, p->sink_index))) - s = pa_sink_get_default(p->core); - - p->sink_index = s ? s->index : PA_IDXSET_INVALID; - return s; -} - -static struct pa_source* get_input_source(struct pa_protocol_esound *p) { - struct pa_source *s; - assert(p); - - if (!(s = pa_idxset_get_by_index(p->core->sources, p->sink_index))) - s = pa_source_get_default(p->core); - - p->source_index = s ? s->index : PA_IDXSET_INVALID; - return s; -} - -static void* connection_write(struct connection *c, size_t length) { - size_t t, i; - assert(c); - - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed); - c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1); - - t = c->write_data_length+length; - - if (c->write_data_alloc < t) - c->write_data = realloc(c->write_data, c->write_data_alloc = t); - - assert(c->write_data); - - i = c->write_data_length; - c->write_data_length += length; - - return c->write_data+i; -} - -/*** esound commands ***/ - -static int esd_proto_connect(struct connection *c, esd_proto_t request, const void *data, size_t length) { - uint32_t ekey; - int *ok; - assert(length == (ESD_KEY_LEN + sizeof(uint32_t))); - - if (!c->authorized) { - if (memcmp(data, c->protocol->esd_key, ESD_KEY_LEN) != 0) { - fprintf(stderr, __FILE__": kicked client with invalid authorization key.\n"); - return -1; - } - - c->authorized = 1; - } - - ekey = *(uint32_t*)(data+ESD_KEY_LEN); - if (ekey == ESD_ENDIAN_KEY) - c->swap_byte_order = 0; - else if (ekey == ESD_SWAP_ENDIAN_KEY) - c->swap_byte_order = 1; - else { - fprintf(stderr, __FILE__": client sent invalid endian key\n"); - return -1; - } - - ok = connection_write(c, sizeof(int)); - assert(ok); - *ok = 1; - return 0; -} - -static int esd_proto_stream_play(struct connection *c, esd_proto_t request, const void *data, size_t length) { - char name[ESD_NAME_MAX]; - int format, rate; - struct pa_sink *sink; - struct pa_sample_spec ss; - size_t l; - assert(c && length == (sizeof(int)*2+ESD_NAME_MAX)); - - format = maybe_swap_endian_32(c->swap_byte_order, *(int*)data); - rate = maybe_swap_endian_32(c->swap_byte_order, *((int*)data + 1)); - - ss.rate = rate; - ss.channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1; - ss.format = ((format & ESD_MASK_BITS) == ESD_BITS16) ? PA_SAMPLE_S16NE : PA_SAMPLE_U8; - - if (!pa_sample_spec_valid(&ss)) - return -1; - - if (!(sink = get_output_sink(c->protocol))) - return -1; - - strncpy(name, data + sizeof(int)*2, sizeof(name)); - name[sizeof(name)-1] = 0; - - pa_client_rename(c->client, name); - - assert(!c->input_memblockq); - - l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS); - c->input_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&ss), l/2, l/PLAYBACK_BUFFER_FRAGMENTS); - assert(c->input_memblockq); - pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2); - c->playback.fragment_size = l/10; - - assert(!c->sink_input); - c->sink_input = pa_sink_input_new(sink, name, &ss); - assert(c->sink_input); - - c->sink_input->owner = c->protocol->module; - c->sink_input->client = c->client; - c->sink_input->peek = sink_input_peek_cb; - c->sink_input->drop = sink_input_drop_cb; - c->sink_input->kill = sink_input_kill_cb; - c->sink_input->get_latency = sink_input_get_latency_cb; - c->sink_input->userdata = c; - - c->state = ESD_STREAMING_DATA; - - c->protocol->n_player++; - - return 0; -} - -static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length) { - char name[ESD_NAME_MAX]; - int format, rate; - struct pa_source *source; - struct pa_sample_spec ss; - size_t l; - assert(c && length == (sizeof(int)*2+ESD_NAME_MAX)); - - format = maybe_swap_endian_32(c->swap_byte_order, *(int*)data); - rate = maybe_swap_endian_32(c->swap_byte_order, *((int*)data + 1)); - - ss.rate = rate; - ss.channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1; - ss.format = ((format & ESD_MASK_BITS) == ESD_BITS16) ? PA_SAMPLE_S16NE : PA_SAMPLE_U8; - - if (!pa_sample_spec_valid(&ss)) - return -1; - - if (request == ESD_PROTO_STREAM_MON) { - struct pa_sink* sink; - - if (!(sink = get_output_sink(c->protocol))) - return -1; - - if (!(source = sink->monitor_source)) - return -1; - } else { - assert(request == ESD_PROTO_STREAM_REC); - - if (!(source = get_input_source(c->protocol))) - return -1; - } - - strncpy(name, data + sizeof(int)*2, sizeof(name)); - name[sizeof(name)-1] = 0; - - pa_client_rename(c->client, name); - - assert(!c->output_memblockq); - - l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS); - c->output_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&ss), 0, 0); - assert(c->output_memblockq); - pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2); - - assert(!c->source_output); - c->source_output = pa_source_output_new(source, name, &ss); - assert(c->source_output); - - c->source_output->owner = c->protocol->module; - c->source_output->client = c->client; - c->source_output->push = source_output_push_cb; - c->source_output->kill = source_output_kill_cb; - c->source_output->userdata = c; - - c->state = ESD_STREAMING_DATA; - - c->protocol->n_player++; - - return 0; -} - -static int esd_proto_get_latency(struct connection *c, esd_proto_t request, const void *data, size_t length) { - struct pa_sink *sink; - int latency, *lag; - assert(c && !data && length == 0); - - if (!(sink = get_output_sink(c->protocol))) - latency = 0; - else { - float usec = pa_sink_get_latency(sink); - usec += PLAYBACK_BUFFER_SECONDS*1000000; /* A better estimation would be a good idea! */ - latency = (int) ((usec*44100)/1000000); - } - - lag = connection_write(c, sizeof(int)); - assert(lag); - *lag = c->swap_byte_order ? swap_endian_32(latency) : latency; - return 0; -} - -static int esd_proto_server_info(struct connection *c, esd_proto_t request, const void *data, size_t length) { - int rate = 44100, format = ESD_STEREO|ESD_BITS16; - int *response; - struct pa_sink *sink; - assert(c && data && length == sizeof(int)); - - if ((sink = get_output_sink(c->protocol))) { - rate = sink->sample_spec.rate; - format = (sink->sample_spec.format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16; - format |= (sink->sample_spec.channels >= 2) ? ESD_STEREO : ESD_MONO; - } - - response = connection_write(c, sizeof(int)*3); - assert(response); - *(response++) = 0; - *(response++) = maybe_swap_endian_32(c->swap_byte_order, rate); - *(response++) = maybe_swap_endian_32(c->swap_byte_order, format); - return 0; -} - -static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length) { - void *response; - size_t t, k, s; - struct connection *conn; - size_t index = PA_IDXSET_INVALID; - assert(c && data && length == sizeof(int)); - - if (esd_proto_server_info(c, request, data, length) < 0) - return -1; - - k = sizeof(int)*5+ESD_NAME_MAX; - s = sizeof(int)*6+ESD_NAME_MAX; - response = connection_write(c, (t = s+k*(c->protocol->n_player+1))); - assert(k); - - for (conn = pa_idxset_first(c->protocol->connections, &index); conn; conn = pa_idxset_next(c->protocol->connections, &index)) { - int format = ESD_BITS16 | ESD_STEREO, rate = 44100, volume = 0xFF; - - if (conn->state != ESD_STREAMING_DATA) - continue; - - assert(t >= s+k+k); - - if (conn->sink_input) { - rate = conn->sink_input->sample_spec.rate; - volume = (conn->sink_input->volume*0xFF)/0x100; - format = (conn->sink_input->sample_spec.format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16; - format |= (conn->sink_input->sample_spec.channels >= 2) ? ESD_STEREO : ESD_MONO; - } - - /* id */ - *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, (int) conn->index); - response += sizeof(int); - - /* name */ - assert(conn->client); - strncpy(response, conn->client->name, ESD_NAME_MAX); - response += ESD_NAME_MAX; - - /* rate */ - *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, rate); - response += sizeof(int); - - /* left */ - *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, volume); - response += sizeof(int); - - /*right*/ - *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, volume); - response += sizeof(int); - - /*format*/ - *((int*) response) = maybe_swap_endian_32(c->swap_byte_order, format); - response += sizeof(int); - - t-= k; - } - - assert(t == s+k); - memset(response, 0, t); - return 0; -} - -static int esd_proto_stream_pan(struct connection *c, esd_proto_t request, const void *data, size_t length) { - int *ok; - uint32_t index, volume; - struct connection *conn; - assert(c && data && length == sizeof(int)*3); - - index = (uint32_t) maybe_swap_endian_32(c->swap_byte_order, *(int*)data); - volume = (uint32_t) maybe_swap_endian_32(c->swap_byte_order, *((int*)data + 1)); - volume = (volume*0x100)/0xFF; - - ok = connection_write(c, sizeof(int)); - assert(ok); - - if ((conn = pa_idxset_get_by_index(c->protocol->connections, index))) { - assert(conn->sink_input); - conn->sink_input->volume = volume; - *ok = 1; - } else - *ok = 0; - - return 0; -} - -/*** client callbacks ***/ - -static void client_kill_cb(struct pa_client *c) { - assert(c && c->userdata); - connection_free(c->userdata); -} - -/*** pa_iochannel callbacks ***/ - -static int do_read(struct connection *c) { - assert(c && c->io); - - if (c->state == ESD_NEXT_REQUEST) { - ssize_t r; - assert(c->read_data_length < sizeof(c->request)); - - if ((r = pa_iochannel_read(c->io, ((void*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) { - fprintf(stderr, "protocol-esound.c: read() failed: %s\n", r == 0 ? "EOF" : strerror(errno)); - return -1; - } - - if ((c->read_data_length+= r) >= sizeof(c->request)) { - struct proto_handler *handler; - - if (c->swap_byte_order) - c->request = swap_endian_32(c->request); - - if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) { - fprintf(stderr, "protocol-esound.c: recieved invalid request.\n"); - return -1; - } - - handler = proto_map+c->request; - - if (!handler->proc) { - fprintf(stderr, "protocol-sound.c: recieved unimplemented request.\n"); - return -1; - } - - if (handler->data_length == 0) { - c->read_data_length = 0; - - if (handler->proc(c, c->request, NULL, 0) < 0) - return -1; - - } else { - if (c->read_data_alloc < handler->data_length) - c->read_data = realloc(c->read_data, c->read_data_alloc = handler->data_length); - assert(c->read_data); - - c->state = ESD_NEEDS_REQDATA; - c->read_data_length = 0; - } - } - - } else if (c->state == ESD_NEEDS_REQDATA) { - ssize_t r; - struct proto_handler *handler = proto_map+c->request; - - assert(handler->proc); - - assert(c->read_data && c->read_data_length < handler->data_length); - - if ((r = pa_iochannel_read(c->io, c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) { - fprintf(stderr, "protocol-esound.c: read() failed: %s\n", r == 0 ? "EOF" : strerror(errno)); - return -1; - } - - if ((c->read_data_length+= r) >= handler->data_length) { - size_t l = c->read_data_length; - assert(handler->proc); - - c->state = ESD_NEXT_REQUEST; - c->read_data_length = 0; - - if (handler->proc(c, c->request, c->read_data, l) < 0) - return -1; - } - } else if (c->state == ESD_STREAMING_DATA && c->sink_input) { - struct pa_memchunk chunk; - ssize_t r; - size_t l; - - assert(c->input_memblockq); - - if (!(l = pa_memblockq_missing(c->input_memblockq))) - return 0; - - if (l > c->playback.fragment_size) - l = c->playback.fragment_size; - - if (c->playback.current_memblock) - if (c->playback.current_memblock->length - c->playback.memblock_index < l) { - pa_memblock_unref(c->playback.current_memblock); - c->playback.current_memblock = NULL; - c->playback.memblock_index = 0; - } - - if (!c->playback.current_memblock) { - c->playback.current_memblock = pa_memblock_new(c->playback.fragment_size*2); - assert(c->playback.current_memblock && c->playback.current_memblock->length >= l); - c->playback.memblock_index = 0; - } - - if ((r = pa_iochannel_read(c->io, c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) { - fprintf(stderr, __FILE__": read() failed: %s\n", r == 0 ? "EOF" : strerror(errno)); - return -1; - } - - chunk.memblock = c->playback.current_memblock; - chunk.index = c->playback.memblock_index; - chunk.length = r; - assert(chunk.memblock); - - c->playback.memblock_index += r; - - assert(c->input_memblockq); - pa_memblockq_push_align(c->input_memblockq, &chunk, 0); - assert(c->sink_input); - pa_sink_notify(c->sink_input->sink); - - } - - return 0; -} - -static int do_write(struct connection *c) { - assert(c && c->io); - - if (c->write_data_length) { - ssize_t r; - - assert(c->write_data_index < c->write_data_length); - if ((r = pa_iochannel_write(c->io, c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) { - fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno)); - return -1; - } - - if ((c->write_data_index +=r) >= c->write_data_length) - c->write_data_length = c->write_data_index = 0; - - } else if (c->state == ESD_STREAMING_DATA && c->source_output) { - struct pa_memchunk chunk; - ssize_t r; - - assert(c->output_memblockq); - if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) - return 0; - - assert(chunk.memblock && chunk.length); - - if ((r = pa_iochannel_write(c->io, chunk.memblock->data+chunk.index, chunk.length)) < 0) { - pa_memblock_unref(chunk.memblock); - fprintf(stderr, __FILE__": write(): %s\n", strerror(errno)); - return -1; - } - - pa_memblockq_drop(c->output_memblockq, r); - pa_memblock_unref(chunk.memblock); - } - - return 0; -} - -static void do_work(struct connection *c) { - assert(c); - - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed); - c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 0); - - if (pa_iochannel_is_hungup(c->io)) - goto fail; - - if (pa_iochannel_is_writable(c->io)) - if (do_write(c) < 0) - goto fail; - - if (pa_iochannel_is_readable(c->io)) - if (do_read(c) < 0) - goto fail; - - return; - -fail: - connection_free(c); -} - -static void io_callback(struct pa_iochannel*io, void *userdata) { - struct connection *c = userdata; - assert(io && c && c->io == io); - - do_work(c); -} - -/*** fixed callback ***/ - -static void fixed_callback(struct pa_mainloop_api*a, void *id, void *userdata) { - struct connection *c = userdata; - assert(a && c && c->fixed_source == id); - - do_work(c); -} - -/*** sink_input callbacks ***/ - -static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk) { - struct connection*c; - assert(i && i->userdata && chunk); - c = i->userdata; - - if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) - return -1; - - return 0; -} - -static void sink_input_drop_cb(struct pa_sink_input *i, size_t length) { - struct connection*c = i->userdata; - assert(i && c && length); - - pa_memblockq_drop(c->input_memblockq, length); - - /* do something */ - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed); - c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1); -} - -static void sink_input_kill_cb(struct pa_sink_input *i) { - assert(i && i->userdata); - connection_free((struct connection *) i->userdata); -} - - -static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i) { - struct connection*c = i->userdata; - assert(i && c); - return pa_samples_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); -} - -/*** source_output callbacks ***/ - -static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk) { - struct connection *c = o->userdata; - assert(o && c && chunk); - - pa_memblockq_push(c->output_memblockq, chunk, 0); - - /* do something */ - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed); - c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1); -} - -static void source_output_kill_cb(struct pa_source_output *o) { - assert(o && o->userdata); - connection_free((struct connection *) o->userdata); -} - -/*** socket server callback ***/ - -static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) { - struct connection *c; - char cname[256]; - assert(s && io && userdata); - - c = malloc(sizeof(struct connection)); - assert(c); - c->protocol = userdata; - c->io = io; - pa_iochannel_set_callback(c->io, io_callback, c); - - pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); - assert(c->protocol->core); - c->client = pa_client_new(c->protocol->core, "ESOUND", cname); - assert(c->client); - c->client->owner = c->protocol->module; - c->client->kill = client_kill_cb; - c->client->userdata = c; - - c->authorized = c->protocol->public; - c->swap_byte_order = 0; - - c->read_data_length = 0; - c->read_data = malloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length); - assert(c->read_data); - - c->write_data_length = c->write_data_index = c->write_data_alloc = 0; - c->write_data = NULL; - - c->state = ESD_NEEDS_REQDATA; - c->request = ESD_PROTO_CONNECT; - - c->sink_input = NULL; - c->input_memblockq = NULL; - - c->source_output = NULL; - c->output_memblockq = NULL; - - c->playback.current_memblock = NULL; - c->playback.memblock_index = 0; - c->playback.fragment_size = 0; - - c->fixed_source = c->protocol->core->mainloop->source_fixed(c->protocol->core->mainloop, fixed_callback, c); - assert(c->fixed_source); - c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 0); - - pa_idxset_put(c->protocol->connections, c, &c->index); -} - -/*** entry points ***/ - -struct pa_protocol_esound* pa_protocol_esound_new(struct pa_core*core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) { - uint32_t source_index, sink_index; - struct pa_protocol_esound *p; - assert(core && server && ma); - - if (pa_modargs_get_source_index(ma, core, &source_index) < 0) { - fprintf(stderr, __FILE__": source does not exist.\n"); - return NULL; - } - - if (pa_modargs_get_sink_index(ma, core, &sink_index) < 0) { - fprintf(stderr, __FILE__": sink does not exist.\n"); - return NULL; - } - p = malloc(sizeof(struct pa_protocol_esound)); - assert(p); - - if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", DEFAULT_COOKIE_FILE), p->esd_key, sizeof(p->esd_key)) < 0) { - free(p); - return NULL; - } - - p->module = m; - p->public = 0; - p->server = server; - pa_socket_server_set_callback(p->server, on_connection, p); - p->core = core; - p->connections = pa_idxset_new(NULL, NULL); - assert(p->connections); - p->sink_index = sink_index; - p->source_index = source_index; - p->n_player = 0; - - return p; -} - -void pa_protocol_esound_free(struct pa_protocol_esound *p) { - struct connection *c; - assert(p); - - while ((c = pa_idxset_first(p->connections, NULL))) - connection_free(c); - - pa_idxset_free(p->connections, NULL, NULL); - pa_socket_server_free(p->server); - free(p); -} diff --git a/src/protocol-esound.h b/src/protocol-esound.h deleted file mode 100644 index b2bdd31b..00000000 --- a/src/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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "core.h" -#include "socket-server.h" -#include "module.h" -#include "modargs.h" - -struct pa_protocol_esound; - -struct pa_protocol_esound* pa_protocol_esound_new(struct pa_core*core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma); -void pa_protocol_esound_free(struct pa_protocol_esound *p); - -#endif diff --git a/src/protocol-native.c b/src/protocol-native.c deleted file mode 100644 index 83c910d1..00000000 --- a/src/protocol-native.c +++ /dev/null @@ -1,863 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#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" - -struct connection; -struct pa_protocol_native; - -struct record_stream { - struct connection *connection; - uint32_t index; - struct pa_source_output *source_output; - struct pa_memblockq *memblockq; - size_t fragment_size; -}; - -struct playback_stream { - struct connection *connection; - uint32_t index; - struct pa_sink_input *sink_input; - struct pa_memblockq *memblockq; - size_t requested_bytes; - int drain_request; - uint32_t drain_tag; -}; - -struct connection { - int authorized; - struct pa_protocol_native *protocol; - struct pa_client *client; - struct pa_pstream *pstream; - struct pa_pdispatch *pdispatch; - struct pa_idxset *record_streams, *playback_streams; - uint32_t rrobin_index; -}; - -struct pa_protocol_native { - struct pa_module *module; - int public; - struct pa_core *core; - struct pa_socket_server *server; - struct pa_idxset *connections; - uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH]; -}; - -static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk); -static void sink_input_drop_cb(struct pa_sink_input *i, size_t length); -static void sink_input_kill_cb(struct pa_sink_input *i); -static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i); - -static void request_bytes(struct playback_stream*s); - -static void source_output_kill_cb(struct pa_source_output *o); -static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk); - -static void command_exit(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); -static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); -static void command_delete_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); -static void command_drain_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); -static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); -static void command_delete_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); -static void command_auth(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); -static void command_set_name(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); -static void command_lookup(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); -static void command_stat(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); -static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata); - -static const struct pa_pdispatch_command command_table[PA_COMMAND_MAX] = { - [PA_COMMAND_ERROR] = { NULL }, - [PA_COMMAND_TIMEOUT] = { NULL }, - [PA_COMMAND_REPLY] = { NULL }, - [PA_COMMAND_CREATE_PLAYBACK_STREAM] = { command_create_playback_stream }, - [PA_COMMAND_DELETE_PLAYBACK_STREAM] = { command_delete_playback_stream }, - [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = { command_drain_playback_stream }, - [PA_COMMAND_CREATE_RECORD_STREAM] = { command_create_record_stream }, - [PA_COMMAND_DELETE_RECORD_STREAM] = { command_delete_record_stream }, - [PA_COMMAND_AUTH] = { command_auth }, - [PA_COMMAND_REQUEST] = { NULL }, - [PA_COMMAND_EXIT] = { command_exit }, - [PA_COMMAND_SET_NAME] = { command_set_name }, - [PA_COMMAND_LOOKUP_SINK] = { command_lookup }, - [PA_COMMAND_LOOKUP_SOURCE] = { command_lookup }, - [PA_COMMAND_STAT] = { command_stat }, - [PA_COMMAND_GET_PLAYBACK_LATENCY] = { command_get_playback_latency }, -}; - -/* structure management */ - -static struct record_stream* record_stream_new(struct connection *c, struct pa_source *source, struct pa_sample_spec *ss, const char *name, size_t maxlength, size_t fragment_size) { - struct record_stream *s; - struct pa_source_output *source_output; - size_t base; - assert(c && source && ss && name && maxlength); - - if (!(source_output = pa_source_output_new(source, name, ss))) - return NULL; - - s = malloc(sizeof(struct record_stream)); - assert(s); - s->connection = c; - s->source_output = source_output; - s->source_output->push = source_output_push_cb; - s->source_output->kill = source_output_kill_cb; - s->source_output->userdata = s; - s->source_output->owner = c->protocol->module; - s->source_output->client = c->client; - - s->memblockq = pa_memblockq_new(maxlength, 0, base = pa_sample_size(ss), 0, 0); - assert(s->memblockq); - - s->fragment_size = (fragment_size/base)*base; - if (!s->fragment_size) - s->fragment_size = base; - - pa_idxset_put(c->record_streams, s, &s->index); - return s; -} - -static void record_stream_free(struct record_stream* r) { - assert(r && r->connection); - - pa_idxset_remove_by_data(r->connection->record_streams, r, NULL); - pa_source_output_free(r->source_output); - pa_memblockq_free(r->memblockq); - free(r); -} - -static struct playback_stream* playback_stream_new(struct connection *c, struct pa_sink *sink, struct pa_sample_spec *ss, const char *name, - size_t maxlength, - size_t tlength, - size_t prebuf, - size_t minreq) { - struct playback_stream *s; - struct pa_sink_input *sink_input; - assert(c && sink && ss && name && maxlength); - - if (!(sink_input = pa_sink_input_new(sink, name, ss))) - return NULL; - - s = malloc(sizeof(struct playback_stream)); - assert (s); - s->connection = c; - s->sink_input = sink_input; - - s->sink_input->peek = sink_input_peek_cb; - s->sink_input->drop = sink_input_drop_cb; - s->sink_input->kill = sink_input_kill_cb; - s->sink_input->get_latency = sink_input_get_latency_cb; - s->sink_input->userdata = s; - s->sink_input->owner = c->protocol->module; - s->sink_input->client = c->client; - - s->memblockq = pa_memblockq_new(maxlength, tlength, pa_sample_size(ss), prebuf, minreq); - assert(s->memblockq); - - s->requested_bytes = 0; - s->drain_request = 0; - - pa_idxset_put(c->playback_streams, s, &s->index); - return s; -} - -static void playback_stream_free(struct playback_stream* p) { - assert(p && p->connection); - - if (p->drain_request) - pa_pstream_send_error(p->connection->pstream, p->drain_tag, PA_ERROR_NOENTITY); - - pa_idxset_remove_by_data(p->connection->playback_streams, p, NULL); - pa_sink_input_free(p->sink_input); - pa_memblockq_free(p->memblockq); - free(p); -} - -static void connection_free(struct connection *c) { - struct record_stream *r; - struct playback_stream *p; - assert(c && c->protocol); - - pa_idxset_remove_by_data(c->protocol->connections, c, NULL); - while ((r = pa_idxset_first(c->record_streams, NULL))) - record_stream_free(r); - pa_idxset_free(c->record_streams, NULL, NULL); - - while ((p = pa_idxset_first(c->playback_streams, NULL))) - playback_stream_free(p); - pa_idxset_free(c->playback_streams, NULL, NULL); - - pa_pdispatch_free(c->pdispatch); - pa_pstream_free(c->pstream); - pa_client_free(c->client); - free(c); -} - -static void request_bytes(struct playback_stream *s) { - struct pa_tagstruct *t; - size_t l; - assert(s); - - if (!(l = pa_memblockq_missing(s->memblockq))) - return; - - if (l <= s->requested_bytes) - return; - - l -= s->requested_bytes; - - if (l < pa_memblockq_get_minreq(s->memblockq)) - return; - - s->requested_bytes += l; - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_REQUEST); - pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ - pa_tagstruct_putu32(t, s->index); - pa_tagstruct_putu32(t, l); - pa_pstream_send_tagstruct(s->connection->pstream, t); - - /*fprintf(stderr, "Requesting %u bytes\n", l);*/ -} - -static void send_memblock(struct connection *c) { - uint32_t start; - struct record_stream *r; - - start = PA_IDXSET_INVALID; - for (;;) { - struct pa_memchunk chunk; - - if (!(r = pa_idxset_rrobin(c->record_streams, &c->rrobin_index))) - return; - - if (start == PA_IDXSET_INVALID) - start = c->rrobin_index; - else if (start == c->rrobin_index) - return; - - if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) { - if (chunk.length > r->fragment_size) - chunk.length = r->fragment_size; - - pa_pstream_send_memblock(c->pstream, r->index, 0, &chunk); - pa_memblockq_drop(r->memblockq, chunk.length); - pa_memblock_unref(chunk.memblock); - - return; - } - } -} - -static void send_playback_stream_killed(struct playback_stream *p) { - struct pa_tagstruct *t; - assert(p); - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_KILLED); - pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ - pa_tagstruct_putu32(t, p->index); - pa_pstream_send_tagstruct(p->connection->pstream, t); -} - -static void send_record_stream_killed(struct record_stream *r) { - struct pa_tagstruct *t; - assert(r); - - t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_KILLED); - pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ - pa_tagstruct_putu32(t, r->index); - pa_pstream_send_tagstruct(r->connection->pstream, t); -} - - -/*** sinkinput callbacks ***/ - -static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk) { - struct playback_stream *s; - assert(i && i->userdata && chunk); - s = i->userdata; - - if (pa_memblockq_peek(s->memblockq, chunk) < 0) - return -1; - - return 0; -} - -static void sink_input_drop_cb(struct pa_sink_input *i, size_t length) { - struct playback_stream *s; - assert(i && i->userdata && length); - s = i->userdata; - - pa_memblockq_drop(s->memblockq, length); - request_bytes(s); - - if (s->drain_request && !pa_memblockq_is_readable(s->memblockq)) { - pa_pstream_send_simple_ack(s->connection->pstream, s->drain_tag); - s->drain_request = 0; - } -} - -static void sink_input_kill_cb(struct pa_sink_input *i) { - assert(i && i->userdata); - send_playback_stream_killed((struct playback_stream *) i->userdata); - playback_stream_free((struct playback_stream *) i->userdata); -} - -static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i) { - struct playback_stream *s; - assert(i && i->userdata); - s = i->userdata; - - return pa_samples_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec); -} - -/*** source_output callbacks ***/ - -static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk) { - struct record_stream *s; - assert(o && o->userdata && chunk); - s = o->userdata; - - pa_memblockq_push(s->memblockq, chunk, 0); - if (!pa_pstream_is_pending(s->connection->pstream)) - send_memblock(s->connection); -} - -static void source_output_kill_cb(struct pa_source_output *o) { - assert(o && o->userdata); - send_record_stream_killed((struct record_stream *) o->userdata); - record_stream_free((struct record_stream *) o->userdata); -} - -/*** pdispatch callbacks ***/ - -static void protocol_error(struct connection *c) { - fprintf(stderr, __FILE__": protocol error, kicking client\n"); - connection_free(c); -} - -static void command_create_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - struct playback_stream *s; - size_t maxlength, tlength, prebuf, minreq; - uint32_t sink_index; - const char *name; - struct pa_sample_spec ss; - struct pa_tagstruct *reply; - struct pa_sink *sink; - assert(c && t && c->protocol && c->protocol->core); - - if (pa_tagstruct_gets(t, &name) < 0 || - pa_tagstruct_get_sample_spec(t, &ss) < 0 || - pa_tagstruct_getu32(t, &sink_index) < 0 || - pa_tagstruct_getu32(t, &maxlength) < 0 || - pa_tagstruct_getu32(t, &tlength) < 0 || - pa_tagstruct_getu32(t, &prebuf) < 0 || - pa_tagstruct_getu32(t, &minreq) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (sink_index == (uint32_t) -1) - sink = pa_sink_get_default(c->protocol->core); - else - sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index); - - if (!sink) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - if (!(s = playback_stream_new(c, sink, &ss, name, maxlength, tlength, prebuf, minreq))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID); - return; - } - - reply = pa_tagstruct_new(NULL, 0); - assert(reply); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - pa_tagstruct_putu32(reply, s->index); - assert(s->sink_input); - pa_tagstruct_putu32(reply, s->sink_input->index); - pa_pstream_send_tagstruct(c->pstream, reply); - request_bytes(s); -} - -static void command_delete_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t channel; - struct playback_stream *s; - assert(c && t); - - if (pa_tagstruct_getu32(t, &channel) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (!(s = pa_idxset_get_by_index(c->playback_streams, channel))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); - return; - } - - playback_stream_free(s); - pa_pstream_send_simple_ack(c->pstream, tag); -} - -static void command_create_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - struct record_stream *s; - size_t maxlength, fragment_size; - uint32_t source_index; - const char *name; - struct pa_sample_spec ss; - struct pa_tagstruct *reply; - struct pa_source *source; - assert(c && t && c->protocol && c->protocol->core); - - if (pa_tagstruct_gets(t, &name) < 0 || - pa_tagstruct_get_sample_spec(t, &ss) < 0 || - pa_tagstruct_getu32(t, &source_index) < 0 || - pa_tagstruct_getu32(t, &maxlength) < 0 || - pa_tagstruct_getu32(t, &fragment_size) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (source_index == (uint32_t) -1) - source = pa_source_get_default(c->protocol->core); - else - source = pa_idxset_get_by_index(c->protocol->core->sources, source_index); - - if (!source) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - if (!(s = record_stream_new(c, source, &ss, name, maxlength, fragment_size))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_INVALID); - return; - } - - reply = pa_tagstruct_new(NULL, 0); - assert(reply); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - pa_tagstruct_putu32(reply, s->index); - assert(s->source_output); - pa_tagstruct_putu32(reply, s->source_output->index); - pa_pstream_send_tagstruct(c->pstream, reply); -} - -static void command_delete_record_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t channel; - struct record_stream *s; - assert(c && t); - - if (pa_tagstruct_getu32(t, &channel) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_EXIST); - return; - } - - record_stream_free(s); - pa_pstream_send_simple_ack(c->pstream, tag); -} - -static void command_exit(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - assert(c && t); - - if (!pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop); - c->protocol->core->mainloop->quit(c->protocol->core->mainloop, 0); - pa_pstream_send_simple_ack(c->pstream, tag); /* nonsense */ - return; -} - -static void command_auth(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - const void*cookie; - assert(c && t); - - if (pa_tagstruct_get_arbitrary(t, &cookie, PA_NATIVE_COOKIE_LENGTH) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) != 0) { - fprintf(stderr, "protocol-native.c: Denied access to client with invalid authorization key.\n"); - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - c->authorized = 1; - pa_pstream_send_simple_ack(c->pstream, tag); - return; -} - -static void command_set_name(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - const char *name; - assert(c && t); - - if (pa_tagstruct_gets(t, &name) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - pa_client_rename(c->client, name); - pa_pstream_send_simple_ack(c->pstream, tag); - return; -} - -static void command_lookup(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - const char *name; - uint32_t index = PA_IDXSET_INVALID; - assert(c && t); - - if (pa_tagstruct_gets(t, &name) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (command == PA_COMMAND_LOOKUP_SINK) { - struct pa_sink *sink; - if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK))) - index = sink->index; - } else { - struct pa_source *source; - assert(command == PA_COMMAND_LOOKUP_SOURCE); - if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE))) - index = source->index; - } - - if (index == PA_IDXSET_INVALID) - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - else { - struct pa_tagstruct *reply; - reply = pa_tagstruct_new(NULL, 0); - assert(reply); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - pa_tagstruct_putu32(reply, index); - pa_pstream_send_tagstruct(c->pstream, reply); - } -} - -static void command_drain_playback_stream(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - uint32_t index; - struct playback_stream *s; - assert(c && t); - - if (pa_tagstruct_getu32(t, &index) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (!(s = pa_idxset_get_by_index(c->playback_streams, index))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - s->drain_request = 0; - - if (!pa_memblockq_is_readable(s->memblockq)) - pa_pstream_send_simple_ack(c->pstream, tag); - else { - s->drain_request = 1; - s->drain_tag = tag; - } -} - -static void command_stat(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - assert(c && t); - struct pa_tagstruct *reply; - - if (!pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - reply = pa_tagstruct_new(NULL, 0); - assert(reply); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - pa_tagstruct_putu32(reply, pa_memblock_get_count()); - pa_tagstruct_putu32(reply, pa_memblock_get_total()); - pa_pstream_send_tagstruct(c->pstream, reply); -} - -static void command_get_playback_latency(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) { - struct connection *c = userdata; - assert(c && t); - struct pa_tagstruct *reply; - struct playback_stream *s; - uint32_t index, latency; - - if (pa_tagstruct_getu32(t, &index) < 0 || - !pa_tagstruct_eof(t)) { - protocol_error(c); - return; - } - - if (!c->authorized) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_ACCESS); - return; - } - - if (!(s = pa_idxset_get_by_index(c->playback_streams, index))) { - pa_pstream_send_error(c->pstream, tag, PA_ERROR_NOENTITY); - return; - } - - latency = pa_sink_input_get_latency(s->sink_input); - reply = pa_tagstruct_new(NULL, 0); - assert(reply); - pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); - pa_tagstruct_putu32(reply, tag); - pa_tagstruct_putu32(reply, latency); - pa_pstream_send_tagstruct(c->pstream, reply); -} - -/*** pstream callbacks ***/ - -static void pstream_packet_callback(struct pa_pstream *p, struct pa_packet *packet, void *userdata) { - struct connection *c = userdata; - assert(p && packet && packet->data && c); - - if (pa_pdispatch_run(c->pdispatch, packet, c) < 0) { - fprintf(stderr, "protocol-native: invalid packet.\n"); - connection_free(c); - } -} - -static void pstream_memblock_callback(struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata) { - struct connection *c = userdata; - struct playback_stream *stream; - assert(p && chunk && userdata); - - if (!(stream = pa_idxset_get_by_index(c->playback_streams, channel))) { - fprintf(stderr, "protocol-native: client sent block for invalid stream.\n"); - connection_free(c); - return; - } - - if (chunk->length >= stream->requested_bytes) - stream->requested_bytes = 0; - else - stream->requested_bytes -= chunk->length; - - pa_memblockq_push_align(stream->memblockq, chunk, delta); - assert(stream->sink_input); - pa_sink_notify(stream->sink_input->sink); - - /*fprintf(stderr, "Recieved %u bytes.\n", chunk->length);*/ -} - -static void pstream_die_callback(struct pa_pstream *p, void *userdata) { - struct connection *c = userdata; - assert(p && c); - connection_free(c); - - fprintf(stderr, "protocol-native: connection died.\n"); -} - - -static void pstream_drain_callback(struct pa_pstream *p, void *userdata) { - struct connection *c = userdata; - assert(p && c); - - send_memblock(c); -} - -/*** client callbacks ***/ - -static void client_kill_cb(struct pa_client *c) { - assert(c && c->userdata); - connection_free(c->userdata); -} - -/*** socket server callbacks ***/ - -static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) { - struct pa_protocol_native *p = userdata; - struct connection *c; - assert(s && io && p); - - c = malloc(sizeof(struct connection)); - assert(c); - c->authorized = p->public; - c->protocol = p; - assert(p->core); - c->client = pa_client_new(p->core, "NATIVE", "Client"); - assert(c->client); - c->client->kill = client_kill_cb; - c->client->userdata = c; - c->client->owner = p->module; - - c->pstream = pa_pstream_new(p->core->mainloop, io); - assert(c->pstream); - - pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c); - pa_pstream_set_recieve_memblock_callback(c->pstream, pstream_memblock_callback, c); - pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c); - pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c); - - c->pdispatch = pa_pdispatch_new(p->core->mainloop, command_table, PA_COMMAND_MAX); - assert(c->pdispatch); - - c->record_streams = pa_idxset_new(NULL, NULL); - c->playback_streams = pa_idxset_new(NULL, NULL); - assert(c->record_streams && c->playback_streams); - - c->rrobin_index = PA_IDXSET_INVALID; - - pa_idxset_put(p->connections, c, NULL); -} - -/*** module entry points ***/ - -struct pa_protocol_native* pa_protocol_native_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) { - struct pa_protocol_native *p; - uint32_t public; - assert(core && server && ma); - - if (pa_modargs_get_value_u32(ma, "public", &public) < 0) { - fprintf(stderr, __FILE__": public= expects numeric argument.\n"); - return NULL; - } - - p = malloc(sizeof(struct pa_protocol_native)); - assert(p); - - if (pa_authkey_load_from_home(pa_modargs_get_value(ma, "cookie", PA_NATIVE_COOKIE_FILE), p->auth_cookie, sizeof(p->auth_cookie)) < 0) { - free(p); - return NULL; - } - - p->module = m; - p->public = public; - p->server = server; - p->core = core; - p->connections = pa_idxset_new(NULL, NULL); - assert(p->connections); - - pa_socket_server_set_callback(p->server, on_connection, p); - - return p; -} - -void pa_protocol_native_free(struct pa_protocol_native *p) { - struct connection *c; - assert(p); - - while ((c = pa_idxset_first(p->connections, NULL))) - connection_free(c); - pa_idxset_free(p->connections, NULL, NULL); - pa_socket_server_free(p->server); - free(p); -} diff --git a/src/protocol-native.h b/src/protocol-native.h deleted file mode 100644 index 3d9fdde1..00000000 --- a/src/protocol-native.h +++ /dev/null @@ -1,35 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "core.h" -#include "socket-server.h" -#include "module.h" -#include "modargs.h" - -struct pa_protocol_native; - -struct pa_protocol_native* pa_protocol_native_new(struct pa_core*core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma); -void pa_protocol_native_free(struct pa_protocol_native *n); - -#endif diff --git a/src/protocol-simple.c b/src/protocol-simple.c deleted file mode 100644 index 3a52e311..00000000 --- a/src/protocol-simple.c +++ /dev/null @@ -1,444 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#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" - -struct connection { - struct pa_protocol_simple *protocol; - struct pa_iochannel *io; - struct pa_sink_input *sink_input; - struct pa_source_output *source_output; - struct pa_client *client; - struct pa_memblockq *input_memblockq, *output_memblockq; - void *fixed_source; - - struct { - struct pa_memblock *current_memblock; - size_t memblock_index, fragment_size; - } playback; -}; - -struct pa_protocol_simple { - struct pa_module *module; - struct pa_core *core; - struct pa_socket_server*server; - struct pa_idxset *connections; - enum { - RECORD = 1, - PLAYBACK = 2, - DUPLEX = 3 - } mode; - struct pa_sample_spec sample_spec; - uint32_t sink_index, source_index; -}; - -#define PLAYBACK_BUFFER_SECONDS (.5) -#define PLAYBACK_BUFFER_FRAGMENTS (10) -#define RECORD_BUFFER_SECONDS (5) -#define RECORD_BUFFER_FRAGMENTS (100) - -static void connection_free(struct connection *c) { - assert(c); - - pa_idxset_remove_by_data(c->protocol->connections, c, NULL); - - if (c->playback.current_memblock) - pa_memblock_unref(c->playback.current_memblock); - if (c->sink_input) - pa_sink_input_free(c->sink_input); - if (c->source_output) - pa_source_output_free(c->source_output); - if (c->client) - pa_client_free(c->client); - if (c->io) - pa_iochannel_free(c->io); - if (c->input_memblockq) - pa_memblockq_free(c->input_memblockq); - if (c->output_memblockq) - pa_memblockq_free(c->output_memblockq); - if (c->fixed_source) - c->protocol->core->mainloop->cancel_fixed(c->protocol->core->mainloop, c->fixed_source); - free(c); -} - -static int do_read(struct connection *c) { - struct pa_memchunk chunk; - ssize_t r; - size_t l; - - if (!c->sink_input || !(l = pa_memblockq_missing(c->input_memblockq))) - return 0; - - if (l > c->playback.fragment_size) - l = c->playback.fragment_size; - - if (c->playback.current_memblock) - if (c->playback.current_memblock->length - c->playback.memblock_index < l) { - pa_memblock_unref(c->playback.current_memblock); - c->playback.current_memblock = NULL; - c->playback.memblock_index = 0; - } - - if (!c->playback.current_memblock) { - c->playback.current_memblock = pa_memblock_new(c->playback.fragment_size*2); - assert(c->playback.current_memblock && c->playback.current_memblock->length >= l); - c->playback.memblock_index = 0; - } - - if ((r = pa_iochannel_read(c->io, c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) { - fprintf(stderr, __FILE__": read() failed: %s\n", r == 0 ? "EOF" : strerror(errno)); - return -1; - } - - chunk.memblock = c->playback.current_memblock; - chunk.index = c->playback.memblock_index; - chunk.length = r; - assert(chunk.memblock); - - c->playback.memblock_index += r; - - assert(c->input_memblockq); - pa_memblockq_push_align(c->input_memblockq, &chunk, 0); - assert(c->sink_input); - pa_sink_notify(c->sink_input->sink); - - return 0; -} - -static int do_write(struct connection *c) { - struct pa_memchunk chunk; - ssize_t r; - - if (!c->source_output) - return 0; - - assert(c->output_memblockq); - if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0) - return 0; - - assert(chunk.memblock && chunk.length); - - if ((r = pa_iochannel_write(c->io, chunk.memblock->data+chunk.index, chunk.length)) < 0) { - pa_memblock_unref(chunk.memblock); - fprintf(stderr, "write(): %s\n", strerror(errno)); - return -1; - } - - pa_memblockq_drop(c->output_memblockq, r); - pa_memblock_unref(chunk.memblock); - - return 0; -} - - -static void do_work(struct connection *c) { - assert(c); - - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed); - c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 0); - - if (pa_iochannel_is_hungup(c->io)) - goto fail; - - if (pa_iochannel_is_writable(c->io)) - if (do_write(c) < 0) - goto fail; - - if (pa_iochannel_is_readable(c->io)) - if (do_read(c) < 0) - goto fail; - - return; - -fail: - connection_free(c); -} - -/*** sink_input callbacks ***/ - -static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk) { - struct connection*c; - assert(i && i->userdata && chunk); - c = i->userdata; - - if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) - return -1; - - return 0; -} - -static void sink_input_drop_cb(struct pa_sink_input *i, size_t length) { - struct connection*c = i->userdata; - assert(i && c && length); - - pa_memblockq_drop(c->input_memblockq, length); - - /* do something */ - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed); - c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1); -} - -static void sink_input_kill_cb(struct pa_sink_input *i) { - assert(i && i->userdata); - connection_free((struct connection *) i->userdata); -} - - -static uint32_t sink_input_get_latency_cb(struct pa_sink_input *i) { - struct connection*c = i->userdata; - assert(i && c); - return pa_samples_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec); -} - -/*** source_output callbacks ***/ - -static void source_output_push_cb(struct pa_source_output *o, const struct pa_memchunk *chunk) { - struct connection *c = o->userdata; - assert(o && c && chunk); - - pa_memblockq_push(c->output_memblockq, chunk, 0); - - /* do something */ - assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->enable_fixed); - c->protocol->core->mainloop->enable_fixed(c->protocol->core->mainloop, c->fixed_source, 1); -} - -static void source_output_kill_cb(struct pa_source_output *o) { - assert(o && o->userdata); - connection_free((struct connection *) o->userdata); -} - -/*** client callbacks ***/ - -static void client_kill_cb(struct pa_client *c) { - assert(c && c->userdata); - connection_free((struct connection *) c->userdata); -} - -/*** pa_iochannel callbacks ***/ - -static void io_callback(struct pa_iochannel*io, void *userdata) { - struct connection *c = userdata; - assert(io && c && c->io == io); - - do_work(c); -} - -/*** fixed callback ***/ - -static void fixed_callback(struct pa_mainloop_api*a, void *id, void *userdata) { - struct connection *c = userdata; - assert(a && c && c->fixed_source == id); - - do_work(c); -} - -/*** socket_server callbacks ***/ - -static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) { - struct pa_protocol_simple *p = userdata; - struct connection *c = NULL; - char cname[256]; - assert(s && io && p); - - c = malloc(sizeof(struct connection)); - assert(c); - c->io = io; - c->sink_input = NULL; - c->source_output = NULL; - c->fixed_source = NULL; - c->input_memblockq = c->output_memblockq = NULL; - c->protocol = p; - c->playback.current_memblock = NULL; - c->playback.memblock_index = 0; - c->playback.fragment_size = 0; - - pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); - c->client = pa_client_new(p->core, "SIMPLE", cname); - assert(c->client); - c->client->owner = p->module; - c->client->kill = client_kill_cb; - c->client->userdata = c; - - if (p->mode & PLAYBACK) { - struct pa_sink *sink; - size_t l; - - if (!(sink = pa_idxset_get_by_index(p->core->sinks, p->sink_index))) - if (!(sink = pa_sink_get_default(p->core))) { - fprintf(stderr, "Failed to get sink.\n"); - goto fail; - } - - c->sink_input = pa_sink_input_new(sink, c->client->name, &p->sample_spec); - if (!c->sink_input) { - fprintf(stderr, "Failed to create sink input.\n"); - goto fail; - } - c->sink_input->owner = p->module; - c->sink_input->client = c->client; - - c->sink_input->peek = sink_input_peek_cb; - c->sink_input->drop = sink_input_drop_cb; - c->sink_input->kill = sink_input_kill_cb; - c->sink_input->get_latency = sink_input_get_latency_cb; - c->sink_input->userdata = c; - - l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS); - c->input_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&p->sample_spec), l/2, l/PLAYBACK_BUFFER_FRAGMENTS); - assert(c->input_memblockq); - pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5); - c->playback.fragment_size = l/10; - } - - if (p->mode & RECORD) { - struct pa_source *source; - size_t l; - - if (!(source = pa_idxset_get_by_index(p->core->sources, p->source_index))) - if (!(source = pa_source_get_default(p->core))) { - fprintf(stderr, "Failed to get source.\n"); - goto fail; - } - - c->source_output = pa_source_output_new(source, c->client->name, &p->sample_spec); - if (!c->source_output) { - fprintf(stderr, "Failed to create source output.\n"); - goto fail; - } - c->source_output->owner = p->module; - c->source_output->client = c->client; - - c->source_output->push = source_output_push_cb; - c->source_output->kill = source_output_kill_cb; - c->source_output->userdata = c; - - l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS); - c->output_memblockq = pa_memblockq_new(l, 0, pa_sample_size(&p->sample_spec), 0, 0); - pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2); - } - - pa_iochannel_set_callback(c->io, io_callback, c); - pa_idxset_put(p->connections, c, NULL); - - c->fixed_source = p->core->mainloop->source_fixed(p->core->mainloop, fixed_callback, c); - assert(c->fixed_source); - p->core->mainloop->enable_fixed(p->core->mainloop, c->fixed_source, 0); - - return; - -fail: - if (c) - connection_free(c); -} - -struct pa_protocol_simple* pa_protocol_simple_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) { - struct pa_protocol_simple* p = NULL; - uint32_t enable; - assert(core && server && ma); - - p = malloc(sizeof(struct pa_protocol_simple)); - assert(p); - memset(p, 0, sizeof(struct pa_protocol_simple)); - - p->module = m; - p->core = core; - p->server = server; - p->connections = pa_idxset_new(NULL, NULL); - - p->sample_spec = core->default_sample_spec; - if (pa_modargs_get_sample_spec(ma, &p->sample_spec) < 0) { - fprintf(stderr, "Failed to parse sample type specification.\n"); - goto fail; - } - - if (pa_modargs_get_source_index(ma, core, &p->source_index) < 0) { - fprintf(stderr, __FILE__": source does not exist.\n"); - goto fail; - } - - if (pa_modargs_get_sink_index(ma, core, &p->sink_index) < 0) { - fprintf(stderr, __FILE__": sink does not exist.\n"); - goto fail; - } - - enable = 0; - if (pa_modargs_get_value_u32(ma, "record", &enable) < 0) { - fprintf(stderr, __FILE__": record= expects a numeric argument.\n"); - goto fail; - } - p->mode = enable ? RECORD : 0; - - enable = 1; - if (pa_modargs_get_value_u32(ma, "playback", &enable) < 0) { - fprintf(stderr, __FILE__": playback= expects a numeric argument.\n"); - goto fail; - } - p->mode |= enable ? PLAYBACK : 0; - - if ((p->mode & (RECORD|PLAYBACK)) == 0) { - fprintf(stderr, __FILE__": neither playback nor recording enabled for protocol.\n"); - goto fail; - } - - pa_socket_server_set_callback(p->server, on_connection, p); - - return p; - -fail: - if (p) - pa_protocol_simple_free(p); - return NULL; -} - - -void pa_protocol_simple_free(struct pa_protocol_simple *p) { - struct connection *c; - assert(p); - - if (p->connections) { - while((c = pa_idxset_first(p->connections, NULL))) - connection_free(c); - - pa_idxset_free(p->connections, NULL, NULL); - } - - if (p->server) - pa_socket_server_free(p->server); - free(p); -} - diff --git a/src/protocol-simple.h b/src/protocol-simple.h deleted file mode 100644 index 0fc1e19d..00000000 --- a/src/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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "socket-server.h" -#include "module.h" -#include "core.h" -#include "modargs.h" - -struct pa_protocol_simple; - -struct pa_protocol_simple* pa_protocol_simple_new(struct pa_core *core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma); -void pa_protocol_simple_free(struct pa_protocol_simple *n); - -#endif diff --git a/src/pstream-util.c b/src/pstream-util.c deleted file mode 100644 index 3957e643..00000000 --- a/src/pstream-util.c +++ /dev/null @@ -1,60 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include "native-common.h" -#include "pstream-util.h" - -void pa_pstream_send_tagstruct(struct pa_pstream *p, struct pa_tagstruct *t) { - size_t length; - uint8_t *data; - struct pa_packet *packet; - assert(p && t); - - data = pa_tagstruct_free_data(t, &length); - assert(data && length); - packet = pa_packet_new_dynamic(data, length); - assert(packet); - pa_pstream_send_packet(p, packet); - pa_packet_unref(packet); -} - -void pa_pstream_send_error(struct pa_pstream *p, uint32_t tag, uint32_t error) { - struct pa_tagstruct *t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_ERROR); - pa_tagstruct_putu32(t, tag); - pa_tagstruct_putu32(t, error); - pa_pstream_send_tagstruct(p, t); -} - -void pa_pstream_send_simple_ack(struct pa_pstream *p, uint32_t tag) { - struct pa_tagstruct *t = pa_tagstruct_new(NULL, 0); - assert(t); - pa_tagstruct_putu32(t, PA_COMMAND_REPLY); - pa_tagstruct_putu32(t, tag); - pa_pstream_send_tagstruct(p, t); -} diff --git a/src/pstream-util.h b/src/pstream-util.h deleted file mode 100644 index b3c89eb0..00000000 --- a/src/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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include "pstream.h" -#include "tagstruct.h" - -/* The tagstruct is freed!*/ -void pa_pstream_send_tagstruct(struct pa_pstream *p, struct pa_tagstruct *t); - -void pa_pstream_send_error(struct pa_pstream *p, uint32_t tag, uint32_t error); -void pa_pstream_send_simple_ack(struct pa_pstream *p, uint32_t tag); - -#endif diff --git a/src/pstream.c b/src/pstream.c deleted file mode 100644 index 3076b776..00000000 --- a/src/pstream.c +++ /dev/null @@ -1,457 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include "pstream.h" -#include "queue.h" - -enum pa_pstream_descriptor_index { - PA_PSTREAM_DESCRIPTOR_LENGTH, - PA_PSTREAM_DESCRIPTOR_CHANNEL, - PA_PSTREAM_DESCRIPTOR_DELTA, - PA_PSTREAM_DESCRIPTOR_MAX -}; - -typedef uint32_t pa_pstream_descriptor[PA_PSTREAM_DESCRIPTOR_MAX]; - -#define PA_PSTREAM_DESCRIPTOR_SIZE (PA_PSTREAM_DESCRIPTOR_MAX*sizeof(uint32_t)) -#define FRAME_SIZE_MAX (1024*64) - -struct item_info { - enum { PA_PSTREAM_ITEM_PACKET, PA_PSTREAM_ITEM_MEMBLOCK } type; - - /* memblock info */ - struct pa_memchunk chunk; - uint32_t channel; - int32_t delta; - - /* packet info */ - struct pa_packet *packet; -}; - -struct pa_pstream { - struct pa_mainloop_api *mainloop; - struct mainloop_source *mainloop_source; - struct pa_iochannel *io; - struct pa_queue *send_queue; - - int in_use, shall_free; - - int dead; - void (*die_callback) (struct pa_pstream *p, void *userdata); - void *die_callback_userdata; - - struct { - struct item_info* current; - pa_pstream_descriptor descriptor; - void *data; - size_t index; - } write; - - struct { - struct pa_memblock *memblock; - struct pa_packet *packet; - pa_pstream_descriptor descriptor; - void *data; - size_t index; - } read; - - void (*recieve_packet_callback) (struct pa_pstream *p, struct pa_packet *packet, void *userdata); - void *recieve_packet_callback_userdata; - - void (*recieve_memblock_callback) (struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata); - void *recieve_memblock_callback_userdata; - - void (*drain_callback)(struct pa_pstream *p, void *userdata); - void *drain_userdata; -}; - -static void do_write(struct pa_pstream *p); -static void do_read(struct pa_pstream *p); - -static void do_something(struct pa_pstream *p) { - assert(p && !p->shall_free); - p->mainloop->enable_fixed(p->mainloop, p->mainloop_source, 0); - - if (p->dead) - return; - - if (pa_iochannel_is_hungup(p->io)) { - p->dead = 1; - if (p->die_callback) - p->die_callback(p, p->die_callback_userdata); - - return; - } - - if (pa_iochannel_is_writable(p->io)) { - p->in_use = 1; - do_write(p); - p->in_use = 0; - - if (p->shall_free) { - pa_pstream_free(p); - return; - } - } - - if (pa_iochannel_is_readable(p->io)) { - p->in_use = 1; - do_read(p); - p->in_use = 0; - if (p->shall_free) { - pa_pstream_free(p); - return; - } - } -} - -static void io_callback(struct pa_iochannel*io, void *userdata) { - struct pa_pstream *p = userdata; - assert(p && p->io == io); - do_something(p); -} - -static void fixed_callback(struct pa_mainloop_api *m, void *id, void*userdata) { - struct pa_pstream *p = userdata; - assert(p && p->mainloop_source == id && p->mainloop == m); - do_something(p); -} - -struct pa_pstream *pa_pstream_new(struct pa_mainloop_api *m, struct pa_iochannel *io) { - struct pa_pstream *p; - assert(io); - - p = malloc(sizeof(struct pa_pstream)); - assert(p); - - p->io = io; - pa_iochannel_set_callback(io, io_callback, p); - - p->dead = 0; - p->die_callback = NULL; - p->die_callback_userdata = NULL; - - p->mainloop = m; - p->mainloop_source = m->source_fixed(m, fixed_callback, p); - m->enable_fixed(m, p->mainloop_source, 0); - - p->send_queue = pa_queue_new(); - assert(p->send_queue); - - p->write.current = NULL; - p->write.index = 0; - - p->read.memblock = NULL; - p->read.packet = NULL; - p->read.index = 0; - - p->recieve_packet_callback = NULL; - p->recieve_packet_callback_userdata = NULL; - - p->recieve_memblock_callback = NULL; - p->recieve_memblock_callback_userdata = NULL; - - p->drain_callback = NULL; - p->drain_userdata = NULL; - - p->in_use = p->shall_free = 0; - - return p; -} - -static void item_free(void *item, void *p) { - struct item_info *i = item; - assert(i); - - if (i->type == PA_PSTREAM_ITEM_MEMBLOCK) { - assert(i->chunk.memblock); - pa_memblock_unref(i->chunk.memblock); - } else { - assert(i->type == PA_PSTREAM_ITEM_PACKET); - assert(i->packet); - pa_packet_unref(i->packet); - } - - free(i); -} - -void pa_pstream_free(struct pa_pstream *p) { - assert(p); - - if (p->in_use) { - /* If this pstream object is used by someone else on the call stack, we have to postpone the freeing */ - p->dead = p->shall_free = 1; - return; - } - - pa_iochannel_free(p->io); - pa_queue_free(p->send_queue, item_free, NULL); - - if (p->write.current) - item_free(p->write.current, NULL); - - if (p->read.memblock) - pa_memblock_unref(p->read.memblock); - - if (p->read.packet) - pa_packet_unref(p->read.packet); - - p->mainloop->cancel_fixed(p->mainloop, p->mainloop_source); - free(p); -} - -void pa_pstream_send_packet(struct pa_pstream*p, struct pa_packet *packet) { - struct item_info *i; - assert(p && packet); - - i = malloc(sizeof(struct item_info)); - assert(i); - i->type = PA_PSTREAM_ITEM_PACKET; - i->packet = pa_packet_ref(packet); - - pa_queue_push(p->send_queue, i); - p->mainloop->enable_fixed(p->mainloop, p->mainloop_source, 1); -} - -void pa_pstream_send_memblock(struct pa_pstream*p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk) { - struct item_info *i; - assert(p && channel != (uint32_t) -1 && chunk); - - i = malloc(sizeof(struct item_info)); - assert(i); - i->type = PA_PSTREAM_ITEM_MEMBLOCK; - i->chunk = *chunk; - i->channel = channel; - i->delta = delta; - - pa_memblock_ref(i->chunk.memblock); - - pa_queue_push(p->send_queue, i); - p->mainloop->enable_fixed(p->mainloop, p->mainloop_source, 1); -} - -void pa_pstream_set_recieve_packet_callback(struct pa_pstream *p, void (*callback) (struct pa_pstream *p, struct pa_packet *packet, void *userdata), void *userdata) { - assert(p && callback); - - p->recieve_packet_callback = callback; - p->recieve_packet_callback_userdata = userdata; -} - -void pa_pstream_set_recieve_memblock_callback(struct pa_pstream *p, void (*callback) (struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata), void *userdata) { - assert(p && callback); - - p->recieve_memblock_callback = callback; - p->recieve_memblock_callback_userdata = userdata; -} - -static void prepare_next_write_item(struct pa_pstream *p) { - assert(p); - - if (!(p->write.current = pa_queue_pop(p->send_queue))) - return; - - p->write.index = 0; - - if (p->write.current->type == PA_PSTREAM_ITEM_PACKET) { - assert(p->write.current->packet); - p->write.data = p->write.current->packet->data; - p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->packet->length); - p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1); - p->write.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA] = 0; - } else { - assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK && p->write.current->chunk.memblock); - p->write.data = p->write.current->chunk.memblock->data + p->write.current->chunk.index; - p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(p->write.current->chunk.length); - p->write.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl(p->write.current->channel); - p->write.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA] = htonl(p->write.current->delta); - } -} - -static void do_write(struct pa_pstream *p) { - void *d; - size_t l; - ssize_t r; - assert(p); - - if (!p->write.current) - prepare_next_write_item(p); - - if (!p->write.current) - return; - - assert(p->write.data); - - if (p->write.index < PA_PSTREAM_DESCRIPTOR_SIZE) { - d = (void*) p->write.descriptor + p->write.index; - l = PA_PSTREAM_DESCRIPTOR_SIZE - p->write.index; - } else { - d = (void*) p->write.data + p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE; - l = ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->write.index - PA_PSTREAM_DESCRIPTOR_SIZE); - } - - if ((r = pa_iochannel_write(p->io, d, l)) < 0) - goto die; - - p->write.index += r; - - if (p->write.index >= PA_PSTREAM_DESCRIPTOR_SIZE+ntohl(p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])) { - assert(p->write.current); - item_free(p->write.current, (void *) 1); - p->write.current = NULL; - - if (p->drain_callback && !pa_pstream_is_pending(p)) - p->drain_callback(p, p->drain_userdata); - } - - return; - -die: - p->dead = 1; - if (p->die_callback) - p->die_callback(p, p->die_callback_userdata); -} - -static void do_read(struct pa_pstream *p) { - void *d; - size_t l; - ssize_t r; - assert(p); - - if (p->read.index < PA_PSTREAM_DESCRIPTOR_SIZE) { - d = (void*) p->read.descriptor + p->read.index; - l = PA_PSTREAM_DESCRIPTOR_SIZE - p->read.index; - } else { - assert(p->read.data); - d = (void*) p->read.data + p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE; - l = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) - (p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE); - } - - if ((r = pa_iochannel_read(p->io, d, l)) <= 0) - goto die; - - p->read.index += r; - - if (p->read.index == PA_PSTREAM_DESCRIPTOR_SIZE) { - /* Reading of frame descriptor complete */ - - /* Frame size too large */ - if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) > FRAME_SIZE_MAX) - goto die; - - assert(!p->read.packet && !p->read.memblock); - - if (ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]) == (uint32_t) -1) { - /* Frame is a packet frame */ - p->read.packet = pa_packet_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])); - assert(p->read.packet); - p->read.data = p->read.packet->data; - } else { - /* Frame is a memblock frame */ - p->read.memblock = pa_memblock_new(ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH])); - assert(p->read.memblock); - p->read.data = p->read.memblock->data; - } - - } else if (p->read.index > PA_PSTREAM_DESCRIPTOR_SIZE) { - /* Frame payload available */ - - if (p->read.memblock && p->recieve_memblock_callback) { /* Is this memblock data? Than pass it to the user */ - size_t l; - - l = (p->read.index - r) < PA_PSTREAM_DESCRIPTOR_SIZE ? p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE : (size_t) r; - - if (l > 0) { - struct pa_memchunk chunk; - - chunk.memblock = p->read.memblock; - chunk.index = p->read.index - PA_PSTREAM_DESCRIPTOR_SIZE - l; - chunk.length = l; - - if (p->recieve_memblock_callback) - p->recieve_memblock_callback( - p, - ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_CHANNEL]), - (int32_t) ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_DELTA]), - &chunk, - p->recieve_memblock_callback_userdata); - } - } - - /* Frame complete */ - if (p->read.index >= ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]) + PA_PSTREAM_DESCRIPTOR_SIZE) { - if (p->read.memblock) { - assert(!p->read.packet); - - pa_memblock_unref(p->read.memblock); - p->read.memblock = NULL; - } else { - assert(p->read.packet); - - if (p->recieve_packet_callback) - p->recieve_packet_callback(p, p->read.packet, p->recieve_packet_callback_userdata); - - pa_packet_unref(p->read.packet); - p->read.packet = NULL; - } - - p->read.index = 0; - } - } - - return; - -die: - p->dead = 1; - if (p->die_callback) - p->die_callback(p, p->die_callback_userdata); - -} - -void pa_pstream_set_die_callback(struct pa_pstream *p, void (*callback)(struct pa_pstream *p, void *userdata), void *userdata) { - assert(p && callback); - p->die_callback = callback; - p->die_callback_userdata = userdata; -} - -int pa_pstream_is_pending(struct pa_pstream *p) { - assert(p); - - if (p->dead) - return 0; - - return p->write.current || !pa_queue_is_empty(p->send_queue); -} - -void pa_pstream_set_drain_callback(struct pa_pstream *p, void (*cb)(struct pa_pstream *p, void *userdata), void *userdata) { - assert(p); - - p->drain_callback = cb; - p->drain_userdata = userdata; -} - diff --git a/src/pstream.h b/src/pstream.h deleted file mode 100644 index 6b91aeb0..00000000 --- a/src/pstream.h +++ /dev/null @@ -1,51 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -#include "packet.h" -#include "memblock.h" -#include "iochannel.h" -#include "mainloop-api.h" -#include "memchunk.h" - -/* It is safe to destroy the calling pstream object from all callbacks */ - -struct pa_pstream; - -struct pa_pstream* pa_pstream_new(struct pa_mainloop_api *m, struct pa_iochannel *io); -void pa_pstream_free(struct pa_pstream*p); - -void pa_pstream_send_packet(struct pa_pstream*p, struct pa_packet *packet); -void pa_pstream_send_memblock(struct pa_pstream*p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk); - -void pa_pstream_set_recieve_packet_callback(struct pa_pstream *p, void (*callback) (struct pa_pstream *p, struct pa_packet *packet, void *userdata), void *userdata); -void pa_pstream_set_recieve_memblock_callback(struct pa_pstream *p, void (*callback) (struct pa_pstream *p, uint32_t channel, int32_t delta, const struct pa_memchunk *chunk, void *userdata), void *userdata); -void pa_pstream_set_drain_callback(struct pa_pstream *p, void (*cb)(struct pa_pstream *p, void *userdata), void *userdata); - -void pa_pstream_set_die_callback(struct pa_pstream *p, void (*callback)(struct pa_pstream *p, void *userdata), void *userdata); - -int pa_pstream_is_pending(struct pa_pstream *p); - -#endif diff --git a/src/queue.c b/src/queue.c deleted file mode 100644 index 9befd475..00000000 --- a/src/queue.c +++ /dev/null @@ -1,109 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include "queue.h" - -struct queue_entry { - struct queue_entry *next; - void *data; -}; - -struct pa_queue { - struct queue_entry *front, *back; - unsigned length; -}; - -struct pa_queue* pa_queue_new(void) { - struct pa_queue *q = malloc(sizeof(struct pa_queue)); - assert(q); - q->front = q->back = NULL; - q->length = 0; - return q; -} - -void pa_queue_free(struct pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata) { - struct queue_entry *e; - assert(q); - - e = q->front; - while (e) { - struct queue_entry *n = e->next; - - if (destroy) - destroy(e->data, userdata); - - free(e); - e = n; - } - - free(q); -} - -void pa_queue_push(struct pa_queue *q, void *p) { - struct queue_entry *e; - - e = malloc(sizeof(struct queue_entry)); - - e->data = p; - e->next = NULL; - - if (q->back) - q->back->next = e; - else { - assert(!q->front); - q->front = e; - } - - q->back = e; - q->length++; -} - -void* pa_queue_pop(struct pa_queue *q) { - void *p; - struct queue_entry *e; - assert(q); - - if (!(e = q->front)) - return NULL; - - q->front = e->next; - if (q->back == e) - q->back = NULL; - - p = e->data; - free(e); - - q->length--; - - return p; -} - -int pa_queue_is_empty(struct pa_queue *q) { - assert(q); - return q->length == 0; -} diff --git a/src/queue.h b/src/queue.h deleted file mode 100644 index 3ec13734..00000000 --- a/src/queue.h +++ /dev/null @@ -1,34 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -struct pa_queue; - -struct pa_queue* pa_queue_new(void); -void pa_queue_free(struct pa_queue* q, void (*destroy)(void *p, void *userdata), void *userdata); -void pa_queue_push(struct pa_queue *q, void *p); -void* pa_queue_pop(struct pa_queue *q); - -int pa_queue_is_empty(struct pa_queue *q); - -#endif diff --git a/src/resampler.c b/src/resampler.c deleted file mode 100644 index 4f5f6be3..00000000 --- a/src/resampler.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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include - -#include "resampler.h" -#include "sconv.h" - -struct pa_resampler { - struct pa_sample_spec i_ss, o_ss; - float* i_buf, *o_buf; - unsigned i_alloc, o_alloc; - size_t i_sz, o_sz; - - int channels; - - pa_convert_to_float32_func_t to_float32_func; - pa_convert_from_float32_func_t from_float32_func; - SRC_STATE *src_state; -}; - -struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const struct pa_sample_spec *b) { - struct pa_resampler *r; - int err; - assert(a && b && pa_sample_spec_valid(a) && pa_sample_spec_valid(b)); - - if (a->channels != b->channels && a->channels != 1 && b->channels != 1) - goto fail; - - if (a->format == PA_SAMPLE_ALAW || a->format == PA_SAMPLE_ULAW || b->format == PA_SAMPLE_ALAW || b->format == PA_SAMPLE_ULAW) - goto fail; - - r = malloc(sizeof(struct pa_resampler)); - assert(r); - - r->channels = a->channels; - if (b->channels < r->channels) - r->channels = b->channels; - - r->i_buf = r->o_buf = NULL; - r->i_alloc = r->o_alloc = 0; - - if (a->rate != b->rate) { - r->src_state = src_new(SRC_SINC_FASTEST, r->channels, &err); - if (err != 0 || !r->src_state) - goto fail; - } else - r->src_state = NULL; - - r->i_ss = *a; - r->o_ss = *b; - - r->i_sz = pa_sample_size(a); - r->o_sz = pa_sample_size(b); - - r->to_float32_func = pa_get_convert_to_float32_function(a->format); - r->from_float32_func = pa_get_convert_from_float32_function(b->format); - - assert(r->to_float32_func && r->from_float32_func); - - return r; - -fail: - if (r) - free(r); - - return NULL; -} - -void pa_resampler_free(struct pa_resampler *r) { - assert(r); - if (r->src_state) - src_delete(r->src_state); - free(r->i_buf); - free(r->o_buf); - free(r); -} - -size_t pa_resampler_request(struct pa_resampler *r, size_t out_length) { - assert(r && (out_length % r->o_sz) == 0); - - return (((out_length / r->o_sz)*r->i_ss.rate)/r->o_ss.rate) * r->i_sz; -} - - -void pa_resampler_run(struct pa_resampler *r, const struct pa_memchunk *in, struct pa_memchunk *out) { - unsigned i_nchannels, o_nchannels, ins, ons, eff_ins, eff_ons; - float *cbuf; - assert(r && in && out && in->length && in->memblock && (in->length % r->i_sz) == 0); - - /* How many input samples? */ - ins = in->length/r->i_sz; - - /* How much space for output samples? */ - if (r->src_state) - ons = (ins*r->o_ss.rate/r->i_ss.rate)+1024; - else - ons = ins; - - /* How many channels? */ - if (r->i_ss.channels == r->o_ss.channels) { - i_nchannels = o_nchannels = 1; - eff_ins = ins*r->i_ss.channels; /* effective samples */ - eff_ons = ons*r->o_ss.channels; - } else { - i_nchannels = r->i_ss.channels; - o_nchannels = r->o_ss.channels; - eff_ins = ins; - eff_ons = ons; - } - - out->memblock = pa_memblock_new(out->length = (ons*r->o_sz)); - out->index = 0; - assert(out->memblock); - - if (r->i_alloc < eff_ins) - r->i_buf = realloc(r->i_buf, sizeof(float) * (r->i_alloc = eff_ins)); - assert(r->i_buf); - - r->to_float32_func(eff_ins, in->memblock->data+in->index, i_nchannels, r->i_buf); - - if (r->src_state) { - int ret; - SRC_DATA data; - - if (r->o_alloc < eff_ons) - r->o_buf = realloc(r->o_buf, sizeof(float) * (r->o_alloc = eff_ons)); - assert(r->o_buf); - - data.data_in = r->i_buf; - data.input_frames = ins; - - data.data_out = r->o_buf; - data.output_frames = ons; - - data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate; - data.end_of_input = 0; - - ret = src_process(r->src_state, &data); - assert(ret == 0); - assert((unsigned) data.input_frames_used == ins); - - cbuf = r->o_buf; - ons = data.output_frames_gen; - - if (r->i_ss.channels == r->o_ss.channels) - eff_ons = ons*r->o_ss.channels; - else - eff_ons = ons; - } else - cbuf = r->i_buf; - - r->from_float32_func(eff_ons, cbuf, out->memblock->data+out->index, o_nchannels); - out->length = ons*r->o_sz; -} diff --git a/src/resampler.h b/src/resampler.h deleted file mode 100644 index 8e979478..00000000 --- a/src/resampler.h +++ /dev/null @@ -1,37 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "sample.h" -#include "memblock.h" -#include "memchunk.h" - -struct pa_resampler; - -struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const struct pa_sample_spec *b); -void pa_resampler_free(struct pa_resampler *r); - -size_t pa_resampler_request(struct pa_resampler *r, size_t out_length); -void pa_resampler_run(struct pa_resampler *r, const struct pa_memchunk *in, struct pa_memchunk *out); - -#endif diff --git a/src/sample-util.c b/src/sample-util.c deleted file mode 100644 index d608ce1b..00000000 --- a/src/sample-util.c +++ /dev/null @@ -1,144 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include "sample-util.h" - -struct pa_memblock *pa_silence_memblock(struct pa_memblock* b, const struct pa_sample_spec *spec) { - assert(b && b->data && spec); - pa_silence_memory(b->data, b->length, spec); - return b; -} - -void pa_silence_memchunk(struct pa_memchunk *c, const struct pa_sample_spec *spec) { - assert(c && c->memblock && c->memblock->data && spec && c->length); - pa_silence_memory(c->memblock->data+c->index, c->length, spec); -} - -void pa_silence_memory(void *p, size_t length, const struct pa_sample_spec *spec) { - char c = 0; - assert(p && length && spec); - - switch (spec->format) { - case PA_SAMPLE_U8: - c = 127; - break; - case PA_SAMPLE_S16LE: - case PA_SAMPLE_S16BE: - case PA_SAMPLE_FLOAT32: - c = 0; - break; - case PA_SAMPLE_ALAW: - case PA_SAMPLE_ULAW: - c = 80; - break; - default: - assert(0); - } - - memset(p, c, length); -} - -size_t pa_mix(struct pa_mix_info channels[], unsigned nchannels, void *data, size_t length, const struct pa_sample_spec *spec, uint32_t volume) { - unsigned c, d; - assert(channels && data && length && spec); - assert(spec->format == PA_SAMPLE_S16NE); - - for (d = 0;; d += sizeof(int16_t)) { - int32_t sum = 0; - - if (d >= length) - return d; - - for (c = 0; c < nchannels; c++) { - int32_t v; - uint32_t volume = channels[c].volume; - - if (d >= channels[c].chunk.length) - return d; - - if (volume == PA_VOLUME_MUTE) - v = 0; - else { - v = *((int16_t*) (channels[c].chunk.memblock->data + channels[c].chunk.index + d)); - - if (volume != PA_VOLUME_NORM) - v = (int32_t) ((float)v*volume/PA_VOLUME_NORM); - } - - sum += v; - } - - if (volume == PA_VOLUME_MUTE) - sum = 0; - else if (volume != PA_VOLUME_NORM) - sum = (int32_t) ((float) sum*volume/PA_VOLUME_NORM); - - if (sum < -0x8000) sum = -0x8000; - if (sum > 0x7FFF) sum = 0x7FFF; - - *((int16_t*) data) = sum; - data += sizeof(int16_t); - } -} - - -void pa_volume_memchunk(struct pa_memchunk*c, const struct pa_sample_spec *spec, uint32_t volume) { - int16_t *d; - size_t n; - assert(c && spec && (c->length % pa_sample_size(spec) == 0)); - assert(spec->format == PA_SAMPLE_S16NE); - - if (volume == PA_VOLUME_NORM) - return; - - if (volume == PA_VOLUME_MUTE) { - pa_silence_memchunk(c, spec); - return; - } - - for (d = (c->memblock->data+c->index), n = c->length/sizeof(int16_t); n > 0; d++, n--) { - int32_t t = (int32_t)(*d); - - t *= volume; - t /= PA_VOLUME_NORM; - - if (t < -0x8000) t = -0x8000; - if (t > 0x7FFF) t = 0x7FFF; - - *d = (int16_t) t; - } -} - -uint32_t pa_volume_multiply(uint32_t a, uint32_t b) { - uint64_t p = a; - p *= b; - p /= PA_VOLUME_NORM; - - return (uint32_t) p; -} diff --git a/src/sample-util.h b/src/sample-util.h deleted file mode 100644 index 73101ab4..00000000 --- a/src/sample-util.h +++ /dev/null @@ -1,48 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "sample.h" -#include "memblock.h" -#include "memchunk.h" - -#define PA_VOLUME_NORM (0x100) -#define PA_VOLUME_MUTE (0) - -struct pa_memblock *pa_silence_memblock(struct pa_memblock* b, const struct pa_sample_spec *spec); -void pa_silence_memchunk(struct pa_memchunk *c, const struct pa_sample_spec *spec); -void pa_silence_memory(void *p, size_t length, const struct pa_sample_spec *spec); - -struct pa_mix_info { - struct pa_memchunk chunk; - uint32_t volume; - void *userdata; -}; - -size_t pa_mix(struct pa_mix_info channels[], unsigned nchannels, void *data, size_t length, const struct pa_sample_spec *spec, uint32_t volume); - -void pa_volume_memchunk(struct pa_memchunk*c, const struct pa_sample_spec *spec, uint32_t volume); - -uint32_t pa_volume_multiply(uint32_t a, uint32_t b); - -#endif diff --git a/src/sample.c b/src/sample.c deleted file mode 100644 index 8179475d..00000000 --- a/src/sample.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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include "sample.h" - -size_t pa_sample_size(const struct pa_sample_spec *spec) { - assert(spec); - size_t b = 1; - - switch (spec->format) { - case PA_SAMPLE_U8: - case PA_SAMPLE_ULAW: - case PA_SAMPLE_ALAW: - b = 1; - break; - case PA_SAMPLE_S16LE: - case PA_SAMPLE_S16BE: - b = 2; - break; - case PA_SAMPLE_FLOAT32LE: - case PA_SAMPLE_FLOAT32BE: - b = 4; - break; - default: - assert(0); - } - - return b * spec->channels; -} - -size_t pa_bytes_per_second(const struct pa_sample_spec *spec) { - assert(spec); - return spec->rate*pa_sample_size(spec); -} - - -uint32_t pa_samples_usec(size_t length, const struct pa_sample_spec *spec) { - assert(spec); - - return (uint32_t) (((double) length /pa_sample_size(spec))/spec->rate*1000000); -} - -int pa_sample_spec_valid(const struct pa_sample_spec *spec) { - assert(spec); - - if (!spec->rate || !spec->channels) - return 0; - - if (spec->format >= PA_SAMPLE_MAX) - return 0; - - return 1; -} - -int pa_sample_spec_equal(const struct pa_sample_spec*a, const struct pa_sample_spec*b) { - assert(a && b); - - return (a->format == b->format) && (a->rate == b->rate) && (a->channels == b->channels); -} - -void pa_sample_snprint(char *s, size_t l, const struct pa_sample_spec *spec) { - static const char* const table[]= { - [PA_SAMPLE_U8] = "U8", - [PA_SAMPLE_ALAW] = "ALAW", - [PA_SAMPLE_ULAW] = "ULAW", - [PA_SAMPLE_S16LE] = "S16LE", - [PA_SAMPLE_S16BE] = "S16BE", - [PA_SAMPLE_FLOAT32LE] = "FLOAT32LE", - [PA_SAMPLE_FLOAT32BE] = "FLOAT32BE", - }; - - assert(pa_sample_spec_valid(spec)); - snprintf(s, l, "%s %uch %uHz", table[spec->format], spec->channels, spec->rate); -} diff --git a/src/sample.h b/src/sample.h deleted file mode 100644 index 825441f2..00000000 --- a/src/sample.h +++ /dev/null @@ -1,64 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include - -enum pa_sample_format { - PA_SAMPLE_U8, - PA_SAMPLE_ALAW, - PA_SAMPLE_ULAW, - PA_SAMPLE_S16LE, - PA_SAMPLE_S16BE, - PA_SAMPLE_FLOAT32LE, - PA_SAMPLE_FLOAT32BE, - PA_SAMPLE_MAX -}; - -#ifdef WORDS_BIGENDIAN -#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE -#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE -#else -#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE -#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE -#endif -#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32NE - -struct pa_sample_spec { - enum pa_sample_format format; - uint32_t rate; - uint8_t channels; -}; - -size_t pa_bytes_per_second(const struct pa_sample_spec *spec); -size_t pa_sample_size(const struct pa_sample_spec *spec); -uint32_t pa_samples_usec(size_t length, const struct pa_sample_spec *spec); -int pa_sample_spec_valid(const struct pa_sample_spec *spec); -int pa_sample_spec_equal(const struct pa_sample_spec*a, const struct pa_sample_spec*b); - - -#define PA_SAMPLE_SNPRINT_MAX_LENGTH 32 -void pa_sample_snprint(char *s, size_t l, const struct pa_sample_spec *spec); - -#endif diff --git a/src/sconv-s16be.c b/src/sconv-s16be.c deleted file mode 100644 index a4c25cde..00000000 --- a/src/sconv-s16be.c +++ /dev/null @@ -1,34 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of polypaudio. - - polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "sconv-s16be.h" - -#define INT16_FROM INT16_FROM_BE -#define INT16_TO INT16_TO_BE - -#define pa_sconv_s16le_to_float32 pa_sconv_s16be_to_float32 -#define pa_sconv_s16le_from_float32 pa_sconv_s16be_from_float32 - -#include "sconv-s16le.c" diff --git a/src/sconv-s16be.h b/src/sconv-s16be.h deleted file mode 100644 index d112d9f2..00000000 --- a/src/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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -void pa_sconv_s16be_to_float32(unsigned n, const void *a, unsigned an, float *b); -void pa_sconv_s16be_from_float32(unsigned n, const float *a, void *b, unsigned bn); - -#endif diff --git a/src/sconv-s16le.c b/src/sconv-s16le.c deleted file mode 100644 index 45b28bdb..00000000 --- a/src/sconv-s16le.c +++ /dev/null @@ -1,82 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include "endianmacros.h" -#include "sconv.h" - -#ifndef INT16_FROM -#define INT16_FROM INT16_FROM_LE -#endif - -#ifndef INT16_TO -#define INT16_TO INT16_TO_LE -#endif - -void pa_sconv_s16le_to_float32(unsigned n, const void *a, unsigned an, float *b) { - const int16_t *ca = a; - assert(n && a && an && b); - - for (; n > 0; n--) { - unsigned i; - float sum = 0; - - for (i = 0; i < an; i++) { - int16_t s = *(ca++); - sum += ((float) INT16_FROM(s))/0x7FFF; - } - - if (sum > 1) - sum = 1; - if (sum < -1) - sum = -1; - - *(b++) = sum; - } -} - -void pa_sconv_s16le_from_float32(unsigned n, const float *a, void *b, unsigned bn) { - int16_t *cb = b; - assert(n && a && b && bn); - - for (; n > 0; n--) { - unsigned i; - int16_t s; - float v = *(a++); - - if (v > 1) - v = 1; - if (v < -1) - v = -1; - - s = (int16_t) (v * 0x7FFF); - s = INT16_TO(s); - - for (i = 0; i < bn; i++) - *(cb++) = s; - } -} diff --git a/src/sconv-s16le.h b/src/sconv-s16le.h deleted file mode 100644 index 0f206ec3..00000000 --- a/src/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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -void pa_sconv_s16le_to_float32(unsigned n, const void *a, unsigned an, float *b); -void pa_sconv_s16le_from_float32(unsigned n, const float *a, void *b, unsigned bn); - -#endif diff --git a/src/sconv.c b/src/sconv.c deleted file mode 100644 index dd9dd241..00000000 --- a/src/sconv.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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include "endianmacros.h" -#include "sconv.h" - -#include "sconv-s16le.h" -#include "sconv-s16be.h" - -static void u8_to_float32(unsigned n, const void *a, unsigned an, float *b) { - unsigned i; - const uint8_t *ca = a; - assert(n && a && an && b); - - for (; n > 0; n--) { - float sum = 0; - - for (i = 0; i < an; i++) { - uint8_t v = *(ca++); - sum += (((float) v)-127)/127; - } - - if (sum > 1) - sum = 1; - if (sum < -1) - sum = -1; - - *(b++) = sum; - } -} - -static void u8_from_float32(unsigned n, const float *a, void *b, unsigned bn) { - unsigned i; - uint8_t *cb = b; - - assert(n && a && b && bn); - for (; n > 0; n--) { - float v = *(a++); - uint8_t u; - - if (v > 1) - v = 1; - - if (v < -1) - v = -1; - - u = (uint8_t) (v*127+127); - - for (i = 0; i < bn; i++) - *(cb++) = u; - } -} - -static void float32_to_float32(unsigned n, const void *a, unsigned an, float *b) { - unsigned i; - const float *ca = a; - assert(n && a && an && b); - for (; n > 0; n--) { - float sum = 0; - - for (i = 0; i < an; i++) - sum += *(ca++); - - if (sum > 1) - sum = 1; - if (sum < -1) - sum = -1; - - *(b++) = sum; - } -} - -static void float32_from_float32(unsigned n, const float *a, void *b, unsigned bn) { - unsigned i; - float *cb = b; - assert(n && a && b && bn); - for (; n > 0; n--) { - float v = *(a++); - for (i = 0; i < bn; i++) - *(cb++) = v; - } -} - -pa_convert_to_float32_func_t pa_get_convert_to_float32_function(enum pa_sample_format f) { - switch(f) { - case PA_SAMPLE_U8: - return u8_to_float32; - case PA_SAMPLE_S16LE: - return pa_sconv_s16le_to_float32; - case PA_SAMPLE_S16BE: - return pa_sconv_s16be_to_float32; - case PA_SAMPLE_FLOAT32: - return float32_to_float32; - default: - return NULL; - } -} - -pa_convert_from_float32_func_t pa_get_convert_from_float32_function(enum pa_sample_format f) { - switch(f) { - case PA_SAMPLE_U8: - return u8_from_float32; - case PA_SAMPLE_S16LE: - return pa_sconv_s16le_from_float32; - case PA_SAMPLE_S16BE: - return pa_sconv_s16be_from_float32; - case PA_SAMPLE_FLOAT32: - return float32_from_float32; - default: - return NULL; - } -} diff --git a/src/sconv.h b/src/sconv.h deleted file mode 100644 index 1a62ed20..00000000 --- a/src/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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include "sample.h" - -typedef void (*pa_convert_to_float32_func_t)(unsigned n, const void *a, unsigned an, float *b); -typedef void (*pa_convert_from_float32_func_t)(unsigned n, const float *a, void *b, unsigned bn); - -pa_convert_to_float32_func_t pa_get_convert_to_float32_function(enum pa_sample_format f); -pa_convert_from_float32_func_t pa_get_convert_from_float32_function(enum pa_sample_format f); - -#endif diff --git a/src/sink-input.c b/src/sink-input.c deleted file mode 100644 index 5c2d3a13..00000000 --- a/src/sink-input.c +++ /dev/null @@ -1,163 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include "sink-input.h" -#include "sample-util.h" - -#define CONVERT_BUFFER_LENGTH 4096 - -struct pa_sink_input* pa_sink_input_new(struct pa_sink *s, const char *name, const struct pa_sample_spec *spec) { - struct pa_sink_input *i; - struct pa_resampler *resampler = NULL; - int r; - char st[256]; - assert(s && spec); - - if (!pa_sample_spec_equal(spec, &s->sample_spec)) - if (!(resampler = pa_resampler_new(spec, &s->sample_spec))) - return NULL; - - i = malloc(sizeof(struct pa_sink_input)); - assert(i); - i->name = name ? strdup(name) : NULL; - i->client = NULL; - i->owner = NULL; - i->sink = s; - i->sample_spec = *spec; - - i->peek = NULL; - i->drop = NULL; - i->kill = NULL; - i->get_latency = NULL; - i->userdata = NULL; - - i->volume = PA_VOLUME_NORM; - - i->resampled_chunk.memblock = NULL; - i->resampled_chunk.index = i->resampled_chunk.length = 0; - i->resampler = resampler; - - assert(s->core); - r = pa_idxset_put(s->core->sink_inputs, i, &i->index); - assert(r == 0 && i->index != PA_IDXSET_INVALID); - r = pa_idxset_put(s->inputs, i, NULL); - assert(r == 0); - - pa_sample_snprint(st, sizeof(st), spec); - fprintf(stderr, "sink-input: created %u \"%s\" on %u with sample spec \"%s\"\n", i->index, i->name, s->index, st); - - return i; -} - -void pa_sink_input_free(struct pa_sink_input* i) { - assert(i); - - assert(i->sink && i->sink->core); - pa_idxset_remove_by_data(i->sink->core->sink_inputs, i, NULL); - pa_idxset_remove_by_data(i->sink->inputs, i, NULL); - - if (i->resampled_chunk.memblock) - pa_memblock_unref(i->resampled_chunk.memblock); - if (i->resampler) - pa_resampler_free(i->resampler); - - free(i->name); - free(i); -} - -void pa_sink_input_kill(struct pa_sink_input*i) { - assert(i); - - if (i->kill) - i->kill(i); -} - -uint32_t pa_sink_input_get_latency(struct pa_sink_input *i) { - uint32_t l = 0; - - assert(i); - if (i->get_latency) - l += i->get_latency(i); - - assert(i->sink); - l += pa_sink_get_latency(i->sink); - - return l; -} - -int pa_sink_input_peek(struct pa_sink_input *i, struct pa_memchunk *chunk) { - assert(i && chunk && i->peek && i->drop); - - if (!i->resampler) - return i->peek(i, chunk); - - if (!i->resampled_chunk.memblock) { - struct pa_memchunk tchunk; - size_t l; - int ret; - - if ((ret = i->peek(i, &tchunk)) < 0) - return ret; - - l = pa_resampler_request(i->resampler, CONVERT_BUFFER_LENGTH); - if (tchunk.length > l) - tchunk.length = l; - - i->drop(i, tchunk.length); - - pa_resampler_run(i->resampler, &tchunk, &i->resampled_chunk); - pa_memblock_unref(tchunk.memblock); - } - - assert(i->resampled_chunk.memblock && i->resampled_chunk.length); - *chunk = i->resampled_chunk; - pa_memblock_ref(i->resampled_chunk.memblock); - return 0; -} - -void pa_sink_input_drop(struct pa_sink_input *i, size_t length) { - assert(i && length); - - if (!i->resampler) { - i->drop(i, length); - return; - } - - assert(i->resampled_chunk.memblock && i->resampled_chunk.length >= length); - - i->resampled_chunk.index += length; - i->resampled_chunk.length -= length; - - if (!i->resampled_chunk.length) { - pa_memblock_unref(i->resampled_chunk.memblock); - i->resampled_chunk.memblock = NULL; - i->resampled_chunk.index = i->resampled_chunk.length = 0; - } -} diff --git a/src/sink-input.h b/src/sink-input.h deleted file mode 100644 index 63dce71d..00000000 --- a/src/sink-input.h +++ /dev/null @@ -1,67 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -#include "sink.h" -#include "sample.h" -#include "memblockq.h" -#include "resampler.h" -#include "module.h" -#include "client.h" - -struct pa_sink_input { - uint32_t index; - - char *name; - struct pa_module *owner; - struct pa_client *client; - struct pa_sink *sink; - struct pa_sample_spec sample_spec; - uint32_t volume; - - int (*peek) (struct pa_sink_input *i, struct pa_memchunk *chunk); - void (*drop) (struct pa_sink_input *i, size_t length); - void (*kill) (struct pa_sink_input *i); - uint32_t (*get_latency) (struct pa_sink_input *i); - - void *userdata; - - struct pa_memchunk resampled_chunk; - struct pa_resampler *resampler; -}; - -struct pa_sink_input* pa_sink_input_new(struct pa_sink *s, const char *name, const struct pa_sample_spec *spec); -void pa_sink_input_free(struct pa_sink_input* i); - -/* Code that didn't create the input stream should call this function to - * request destruction of it */ -void pa_sink_input_kill(struct pa_sink_input *i); - -uint32_t pa_sink_input_get_latency(struct pa_sink_input *i); - -int pa_sink_input_peek(struct pa_sink_input *i, struct pa_memchunk *chunk); -void pa_sink_input_drop(struct pa_sink_input *i, size_t length); - -#endif diff --git a/src/sink.c b/src/sink.c deleted file mode 100644 index 20fa76a6..00000000 --- a/src/sink.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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include "sink.h" -#include "sink-input.h" -#include "namereg.h" -#include "util.h" -#include "sample-util.h" - -#define MAX_MIX_CHANNELS 32 - -struct pa_sink* pa_sink_new(struct pa_core *core, const char *name, int fail, const struct pa_sample_spec *spec) { - struct pa_sink *s; - char *n = NULL; - char st[256]; - int r; - assert(core && name && spec); - - s = malloc(sizeof(struct pa_sink)); - assert(s); - - if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) { - free(s); - return NULL; - } - - s->name = strdup(name); - s->description = NULL; - - s->owner = NULL; - s->core = core; - s->sample_spec = *spec; - s->inputs = pa_idxset_new(NULL, NULL); - - n = pa_sprintf_malloc("%s_monitor", name); - s->monitor_source = pa_source_new(core, n, 0, spec); - assert(s->monitor_source); - free(n); - s->monitor_source->monitor_of = s; - - s->volume = PA_VOLUME_NORM; - - s->notify = NULL; - s->get_latency = NULL; - s->userdata = NULL; - - r = pa_idxset_put(core->sinks, s, &s->index); - assert(s->index != PA_IDXSET_INVALID && r >= 0); - - pa_sample_snprint(st, sizeof(st), spec); - fprintf(stderr, "sink: created %u \"%s\" with sample spec \"%s\"\n", s->index, s->name, st); - - return s; -} - -void pa_sink_free(struct pa_sink *s) { - struct pa_sink_input *i, *j = NULL; - assert(s); - - pa_namereg_unregister(s->core, s->name); - - while ((i = pa_idxset_first(s->inputs, NULL))) { - assert(i != j); - pa_sink_input_kill(i); - j = i; - } - pa_idxset_free(s->inputs, NULL, NULL); - - pa_source_free(s->monitor_source); - pa_idxset_remove_by_data(s->core->sinks, s, NULL); - - fprintf(stderr, "sink: freed %u \"%s\"\n", s->index, s->name); - - free(s->name); - free(s->description); - free(s); -} - -void pa_sink_notify(struct pa_sink*s) { - assert(s); - - if (s->notify) - s->notify(s); -} - -static unsigned fill_mix_info(struct pa_sink *s, struct pa_mix_info *info, unsigned maxinfo) { - uint32_t index = PA_IDXSET_INVALID; - struct pa_sink_input *i; - unsigned n = 0; - - assert(s && info); - - for (i = pa_idxset_first(s->inputs, &index); maxinfo > 0 && i; i = pa_idxset_next(s->inputs, &index)) { - if (pa_sink_input_peek(i, &info->chunk) < 0) - continue; - - info->volume = i->volume; - - assert(info->chunk.memblock && info->chunk.memblock->data && info->chunk.length); - info->userdata = i; - - info++; - maxinfo--; - n++; - } - - return n; -} - -static void inputs_drop(struct pa_sink *s, struct pa_mix_info *info, unsigned maxinfo, size_t length) { - assert(s && info); - - for (; maxinfo > 0; maxinfo--, info++) { - struct pa_sink_input *i = info->userdata; - assert(i && info->chunk.memblock); - - pa_memblock_unref(info->chunk.memblock); - pa_sink_input_drop(i, length); - } -} - -int pa_sink_render(struct pa_sink*s, size_t length, struct pa_memchunk *result) { - struct pa_mix_info info[MAX_MIX_CHANNELS]; - unsigned n; - size_t l; - assert(s && length && result); - - n = fill_mix_info(s, info, MAX_MIX_CHANNELS); - - if (n <= 0) - return -1; - - if (n == 1) { - uint32_t volume = PA_VOLUME_NORM; - struct pa_sink_input *i = info[0].userdata; - assert(i); - *result = info[0].chunk; - pa_memblock_ref(result->memblock); - - if (result->length > length) - result->length = length; - - l = result->length; - - if (s->volume != PA_VOLUME_NORM || info[0].volume != PA_VOLUME_NORM) - volume = pa_volume_multiply(s->volume, info[0].volume); - - if (volume != PA_VOLUME_NORM) { - pa_memchunk_make_writable(result); - pa_volume_memchunk(result, &s->sample_spec, volume); - } - } else { - result->memblock = pa_memblock_new(length); - assert(result->memblock); - - result->length = l = pa_mix(info, n, result->memblock->data, length, &s->sample_spec, s->volume); - result->index = 0; - - assert(l); - } - - inputs_drop(s, info, n, l); - - assert(s->monitor_source); - pa_source_post(s->monitor_source, result); - - return 0; -} - -int pa_sink_render_into(struct pa_sink*s, struct pa_memchunk *target) { - struct pa_mix_info info[MAX_MIX_CHANNELS]; - unsigned n; - size_t l; - assert(s && target && target->length && target->memblock && target->memblock->data); - - n = fill_mix_info(s, info, MAX_MIX_CHANNELS); - - if (n <= 0) - return -1; - - if (n == 1) { - uint32_t volume = PA_VOLUME_NORM; - struct pa_sink_info *i = info[0].userdata; - assert(i); - - l = target->length; - if (l > info[0].chunk.length) - l = info[0].chunk.length; - - memcpy(target->memblock->data+target->index, info[0].chunk.memblock->data + info[0].chunk.index, l); - target->length = l; - - if (s->volume != PA_VOLUME_NORM || info[0].volume != PA_VOLUME_NORM) - volume = pa_volume_multiply(s->volume, info[0].volume); - - if (volume != PA_VOLUME_NORM) - pa_volume_memchunk(target, &s->sample_spec, volume); - } else - target->length = l = pa_mix(info, n, target->memblock->data+target->index, target->length, &s->sample_spec, s->volume); - - assert(l); - inputs_drop(s, info, n, l); - - assert(s->monitor_source); - pa_source_post(s->monitor_source, target); - - return 0; -} - -void pa_sink_render_into_full(struct pa_sink *s, struct pa_memchunk *target) { - struct pa_memchunk chunk; - size_t l, d; - assert(s && target && target->memblock && target->length && target->memblock->data); - - l = target->length; - d = 0; - while (l > 0) { - chunk = *target; - chunk.index += d; - chunk.length -= d; - - if (pa_sink_render_into(s, &chunk) < 0) - break; - - d += chunk.length; - l -= chunk.length; - } - - if (l > 0) { - chunk = *target; - chunk.index += d; - chunk.length -= d; - pa_silence_memchunk(&chunk, &s->sample_spec); - } -} - -uint32_t pa_sink_get_latency(struct pa_sink *s) { - assert(s); - - if (!s->get_latency) - return 0; - - return s->get_latency(s); -} - -struct pa_sink* pa_sink_get_default(struct pa_core *c) { - struct pa_sink *sink; - assert(c); - - if ((sink = pa_idxset_get_by_index(c->sinks, c->default_sink_index))) - return sink; - - if (!(sink = pa_idxset_first(c->sinks, &c->default_sink_index))) - return NULL; - - fprintf(stderr, "core: default sink vanished, setting to %u.\n", sink->index); - return sink; -} - -void pa_sink_set_owner(struct pa_sink *sink, struct pa_module *m) { - sink->owner = m; - - if (sink->monitor_source) - pa_source_set_owner(sink->monitor_source, m); -} diff --git a/src/sink.h b/src/sink.h deleted file mode 100644 index 2b5d9495..00000000 --- a/src/sink.h +++ /dev/null @@ -1,67 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -struct pa_sink; - -#include - -#include "core.h" -#include "sample.h" -#include "idxset.h" -#include "source.h" - -struct pa_sink { - uint32_t index; - - char *name, *description; - struct pa_module *owner; - struct pa_core *core; - struct pa_sample_spec sample_spec; - struct pa_idxset *inputs; - - struct pa_source *monitor_source; - - uint32_t volume; - - void (*notify)(struct pa_sink*sink); - uint32_t (*get_latency)(struct pa_sink *s); - void *userdata; -}; - -struct pa_sink* pa_sink_new(struct pa_core *core, const char *name, int fail, const struct pa_sample_spec *spec); -void pa_sink_free(struct pa_sink* s); - -int pa_sink_render(struct pa_sink*s, size_t length, struct pa_memchunk *result); -int pa_sink_render_into(struct pa_sink*s, struct pa_memchunk *target); -void pa_sink_render_into_full(struct pa_sink *s, struct pa_memchunk *target); - -uint32_t pa_sink_get_latency(struct pa_sink *s); - -void pa_sink_notify(struct pa_sink*s); - -struct pa_sink* pa_sink_get_default(struct pa_core *c); - -void pa_sink_set_owner(struct pa_sink *sink, struct pa_module *m); - -#endif diff --git a/src/sioman.c b/src/sioman.c deleted file mode 100644 index 999b8a5c..00000000 --- a/src/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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#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/sioman.h b/src/sioman.h deleted file mode 100644 index 1b60d4a9..00000000 --- a/src/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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -int pa_stdio_acquire(void); -void pa_stdio_release(void); - -#endif diff --git a/src/socket-client.c b/src/socket-client.c deleted file mode 100644 index a2187e6a..00000000 --- a/src/socket-client.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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "socket-client.h" -#include "socket-util.h" -#include "util.h" - -struct pa_socket_client { - struct pa_mainloop_api *mainloop; - int fd; - - void *io_source, *fixed_source; - void (*callback)(struct pa_socket_client*c, struct pa_iochannel *io, void *userdata); - void *userdata; -}; - -static struct pa_socket_client*pa_socket_client_new(struct pa_mainloop_api *m) { - struct pa_socket_client *c; - assert(m); - - c = malloc(sizeof(struct pa_socket_client)); - assert(c); - c->mainloop = m; - c->fd = -1; - c->io_source = c->fixed_source = NULL; - c->callback = NULL; - c->userdata = NULL; - return c; -} - -static void do_call(struct pa_socket_client *c) { - struct pa_iochannel *io; - int error, lerror; - assert(c && c->callback); - - lerror = sizeof(error); - if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &error, &lerror) < 0) { - fprintf(stderr, "getsockopt(): %s\n", strerror(errno)); - goto failed; - } - - if (lerror != sizeof(error)) { - fprintf(stderr, "getsocktop() returned invalid size.\n"); - goto failed; - } - - if (error != 0) { - fprintf(stderr, "connect(): %s\n", strerror(error)); - goto failed; - } - - io = pa_iochannel_new(c->mainloop, c->fd, c->fd); - assert(io); - c->fd = -1; - c->callback(c, io, c->userdata); - - return; - -failed: - close(c->fd); - c->fd = -1; - c->callback(c, NULL, c->userdata); - return; -} - -static void connect_fixed_cb(struct pa_mainloop_api *m, void *id, void *userdata) { - struct pa_socket_client *c = userdata; - assert(m && c && c->fixed_source == id); - m->cancel_fixed(m, c->fixed_source); - c->fixed_source = NULL; - do_call(c); -} - -static void connect_io_cb(struct pa_mainloop_api*m, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { - struct pa_socket_client *c = userdata; - assert(m && c && c->io_source == id && fd >= 0); - m->cancel_io(m, c->io_source); - c->io_source = NULL; - do_call(c); -} - -static int do_connect(struct pa_socket_client *c, const struct sockaddr *sa, socklen_t len) { - int r; - assert(c && sa && len); - - pa_make_nonblock_fd(c->fd); - - if ((r = connect(c->fd, sa, len)) < 0) { - if (errno != EINPROGRESS) { - fprintf(stderr, "connect(): %s\n", strerror(errno)); - return -1; - } - - c->io_source = c->mainloop->source_io(c->mainloop, c->fd, PA_MAINLOOP_API_IO_EVENT_OUTPUT, connect_io_cb, c); - assert(c->io_source); - } else { - c->fixed_source = c->mainloop->source_fixed(c->mainloop, connect_fixed_cb, c); - assert(c->fixed_source); - } - - return 0; -} - -struct pa_socket_client* pa_socket_client_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port) { - struct pa_socket_client *c; - struct sockaddr_in sa; - assert(m && address && port); - - c = pa_socket_client_new(m); - assert(c); - - if ((c->fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { - fprintf(stderr, "socket(): %s\n", strerror(errno)); - goto fail; - } - - pa_socket_tcp_low_delay(c->fd); - - sa.sin_family = AF_INET; - sa.sin_port = htons(port); - sa.sin_addr.s_addr = htonl(address); - - if (do_connect(c, (struct sockaddr*) &sa, sizeof(sa)) < 0) - goto fail; - - return c; - -fail: - pa_socket_client_free(c); - return NULL; -} - -struct pa_socket_client* pa_socket_client_new_unix(struct pa_mainloop_api *m, const char *filename) { - struct pa_socket_client *c; - struct sockaddr_un sa; - assert(m && filename); - - c = pa_socket_client_new(m); - assert(c); - - if ((c->fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { - fprintf(stderr, "socket(): %s\n", strerror(errno)); - goto fail; - } - - pa_socket_low_delay(c->fd); - - sa.sun_family = AF_LOCAL; - strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1); - sa.sun_path[sizeof(sa.sun_path) - 1] = 0; - - if (do_connect(c, (struct sockaddr*) &sa, sizeof(sa)) < 0) - goto fail; - - return c; - -fail: - pa_socket_client_free(c); - return NULL; -} - -struct pa_socket_client* pa_socket_client_new_sockaddr(struct pa_mainloop_api *m, const struct sockaddr *sa, size_t salen) { - struct pa_socket_client *c; - assert(m && sa); - c = pa_socket_client_new(m); - assert(c); - - if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) { - fprintf(stderr, "socket(): %s\n", strerror(errno)); - goto fail; - } - - if (sa->sa_family == AF_INET) - pa_socket_tcp_low_delay(c->fd); - else - pa_socket_low_delay(c->fd); - - if (do_connect(c, sa, salen) < 0) - goto fail; - - return c; - -fail: - pa_socket_client_free(c); - return NULL; - -} - -void pa_socket_client_free(struct pa_socket_client *c) { - assert(c && c->mainloop); - if (c->io_source) - c->mainloop->cancel_io(c->mainloop, c->io_source); - if (c->fixed_source) - c->mainloop->cancel_fixed(c->mainloop, c->fixed_source); - if (c->fd >= 0) - close(c->fd); - free(c); -} - -void pa_socket_client_set_callback(struct pa_socket_client *c, void (*on_connection)(struct pa_socket_client *c, struct pa_iochannel*io, void *userdata), void *userdata) { - assert(c); - c->callback = on_connection; - c->userdata = userdata; -} diff --git a/src/socket-client.h b/src/socket-client.h deleted file mode 100644 index 2a89210e..00000000 --- a/src/socket-client.h +++ /dev/null @@ -1,41 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include "mainloop-api.h" -#include "iochannel.h" - -/* It is safe to destroy the calling socket_client object from the callback */ - -struct pa_socket_client; - -struct pa_socket_client* pa_socket_client_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port); -struct pa_socket_client* pa_socket_client_new_unix(struct pa_mainloop_api *m, const char *filename); -struct pa_socket_client* pa_socket_client_new_sockaddr(struct pa_mainloop_api *m, const struct sockaddr *sa, size_t salen); - -void pa_socket_client_free(struct pa_socket_client *c); - -void pa_socket_client_set_callback(struct pa_socket_client *c, void (*on_connection)(struct pa_socket_client *c, struct pa_iochannel*io, void *userdata), void *userdata); - -#endif diff --git a/src/socket-server.c b/src/socket-server.c deleted file mode 100644 index 0f497377..00000000 --- a/src/socket-server.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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "socket-server.h" -#include "socket-util.h" - -struct pa_socket_server { - int fd; - char *filename; - - void (*on_connection)(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata); - void *userdata; - - void *mainloop_source; - struct pa_mainloop_api *mainloop; - enum { SOCKET_SERVER_GENERIC, SOCKET_SERVER_IPV4, SOCKET_SERVER_UNIX } type; -}; - -static void callback(struct pa_mainloop_api *mainloop, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) { - struct pa_socket_server *s = userdata; - struct pa_iochannel *io; - int nfd; - assert(s && s->mainloop == mainloop && s->mainloop_source == id && id && fd >= 0 && fd == s->fd && events == PA_MAINLOOP_API_IO_EVENT_INPUT); - - if ((nfd = accept(fd, NULL, NULL)) < 0) { - fprintf(stderr, "accept(): %s\n", strerror(errno)); - return; - } - - if (!s->on_connection) { - close(nfd); - return; - } - - /* There should be a check for socket type here */ - if (s->type == SOCKET_SERVER_IPV4) - pa_socket_tcp_low_delay(fd); - else - pa_socket_low_delay(fd); - - io = pa_iochannel_new(s->mainloop, nfd, nfd); - assert(io); - s->on_connection(s, io, s->userdata); -} - -struct pa_socket_server* pa_socket_server_new(struct pa_mainloop_api *m, int fd) { - struct pa_socket_server *s; - assert(m && fd >= 0); - - s = malloc(sizeof(struct pa_socket_server)); - assert(s); - s->fd = fd; - s->filename = NULL; - s->on_connection = NULL; - s->userdata = NULL; - - s->mainloop = m; - s->mainloop_source = m->source_io(m, fd, PA_MAINLOOP_API_IO_EVENT_INPUT, callback, s); - assert(s->mainloop_source); - - s->type = SOCKET_SERVER_GENERIC; - - return s; -} - -struct pa_socket_server* pa_socket_server_new_unix(struct pa_mainloop_api *m, const char *filename) { - int fd = -1; - struct sockaddr_un sa; - struct pa_socket_server *s; - - assert(m && filename); - - if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { - fprintf(stderr, "socket(): %s\n", strerror(errno)); - goto fail; - } - - sa.sun_family = AF_LOCAL; - strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1); - sa.sun_path[sizeof(sa.sun_path) - 1] = 0; - - pa_socket_low_delay(fd); - - if (bind(fd, (struct sockaddr*) &sa, SUN_LEN(&sa)) < 0) { - fprintf(stderr, "bind(): %s\n", strerror(errno)); - goto fail; - } - - if (listen(fd, 5) < 0) { - fprintf(stderr, "listen(): %s\n", strerror(errno)); - goto fail; - } - - s = pa_socket_server_new(m, fd); - assert(s); - - s->filename = strdup(filename); - assert(s->filename); - - s->type = SOCKET_SERVER_UNIX; - - return s; - -fail: - if (fd >= 0) - close(fd); - - return NULL; -} - -struct pa_socket_server* pa_socket_server_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port) { - struct pa_socket_server *ss; - int fd = -1; - struct sockaddr_in sa; - int on = 1; - - assert(m && port); - - if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { - fprintf(stderr, "socket(): %s\n", strerror(errno)); - goto fail; - } - - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) - fprintf(stderr, "setsockopt(): %s\n", strerror(errno)); - - pa_socket_tcp_low_delay(fd); - - sa.sin_family = AF_INET; - sa.sin_port = htons(port); - sa.sin_addr.s_addr = htonl(address); - - if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { - fprintf(stderr, "bind(): %s\n", strerror(errno)); - goto fail; - } - - if (listen(fd, 5) < 0) { - fprintf(stderr, "listen(): %s\n", strerror(errno)); - goto fail; - } - - if ((ss = pa_socket_server_new(m, fd))) - ss->type = SOCKET_SERVER_IPV4; - - return ss; - -fail: - if (fd >= 0) - close(fd); - - return NULL; -} - -void pa_socket_server_free(struct pa_socket_server*s) { - assert(s); - close(s->fd); - - if (s->filename) { - unlink(s->filename); - free(s->filename); - } - - - s->mainloop->cancel_io(s->mainloop, s->mainloop_source); - - free(s); -} - -void pa_socket_server_set_callback(struct pa_socket_server*s, void (*on_connection)(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata), void *userdata) { - assert(s); - - s->on_connection = on_connection; - s->userdata = userdata; -} diff --git a/src/socket-server.h b/src/socket-server.h deleted file mode 100644 index 6661a66e..00000000 --- a/src/socket-server.h +++ /dev/null @@ -1,41 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include "mainloop-api.h" -#include "iochannel.h" - -/* It is safe to destroy the calling socket_server object from the callback */ - -struct pa_socket_server; - -struct pa_socket_server* pa_socket_server_new(struct pa_mainloop_api *m, int fd); -struct pa_socket_server* pa_socket_server_new_unix(struct pa_mainloop_api *m, const char *filename); -struct pa_socket_server* pa_socket_server_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port); - -void pa_socket_server_free(struct pa_socket_server*s); - -void pa_socket_server_set_callback(struct pa_socket_server*s, void (*on_connection)(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata), void *userdata); - -#endif diff --git a/src/socket-util.c b/src/socket-util.c deleted file mode 100644 index e0a3c28d..00000000 --- a/src/socket-util.c +++ /dev/null @@ -1,216 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "socket-util.h" -#include "util.h" - -void pa_socket_peer_to_string(int fd, char *c, size_t l) { - struct stat st; - - assert(c && l && fd >= 0); - - if (fstat(fd, &st) < 0) { - snprintf(c, l, "Invalid client fd"); - return; - } - - if (S_ISSOCK(st.st_mode)) { - union { - struct sockaddr sa; - struct sockaddr_in in; - struct sockaddr_un un; - } sa; - socklen_t sa_len = sizeof(sa); - - if (getpeername(fd, &sa.sa, &sa_len) >= 0) { - - if (sa.sa.sa_family == AF_INET) { - uint32_t ip = ntohl(sa.in.sin_addr.s_addr); - - snprintf(c, l, "TCP/IP client from %i.%i.%i.%i:%u", - ip >> 24, - (ip >> 16) & 0xFF, - (ip >> 8) & 0xFF, - ip & 0xFF, - ntohs(sa.in.sin_port)); - return; - } else if (sa.sa.sa_family == AF_LOCAL) { - snprintf(c, l, "UNIX socket client"); - return; - } - - } - snprintf(c, l, "Unknown network client"); - return; - } else if (S_ISCHR(st.st_mode) && (fd == 0 || fd == 1)) { - snprintf(c, l, "STDIN/STDOUT client"); - return; - } - - snprintf(c, l, "Unknown client"); -} - -int pa_socket_low_delay(int fd) { - int priority; - assert(fd >= 0); - - priority = 7; - if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) - return -1; - - return 0; -} - -int pa_socket_tcp_low_delay(int fd) { - int ret, tos; - - assert(fd >= 0); - - ret = pa_socket_low_delay(fd); - -/* on = 1; */ -/* if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) */ -/* ret = -1; */ - - tos = IPTOS_LOWDELAY; - if (setsockopt(fd, SOL_IP, IP_TOS, &tos, sizeof(tos)) < 0) - ret = -1; - - return ret; - -} - -int pa_socket_set_rcvbuf(int fd, size_t l) { - assert(fd >= 0); - - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &l, sizeof(l)) < 0) - return -1; - - return 0; -} - -int pa_socket_set_sndbuf(int fd, size_t l) { - assert(fd >= 0); - - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &l, sizeof(l)) < 0) - return -1; - - return 0; -} - -int pa_unix_socket_is_stale(const char *fn) { - struct sockaddr_un sa; - int fd = -1, ret = -1; - - if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { - fprintf(stderr, "socket(): %s\n", strerror(errno)); - goto finish; - } - - sa.sun_family = AF_LOCAL; - strncpy(sa.sun_path, fn, sizeof(sa.sun_path)-1); - sa.sun_path[sizeof(sa.sun_path) - 1] = 0; - - if (connect(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) { - if (errno == ECONNREFUSED) - ret = 1; - } else - ret = 0; - -finish: - if (fd >= 0) - close(fd); - - return ret; -} - -int pa_unix_socket_remove_stale(const char *fn) { - int r; - - if ((r = pa_unix_socket_is_stale(fn)) < 0) - return errno != ENOENT ? -1 : 0; - - if (!r) - return 0; - - /* Yes, here is a race condition. But who cares? */ - if (unlink(fn) < 0) - return -1; - - return 0; -} - -int pa_unix_socket_make_secure_dir(const char *fn) { - int ret = -1; - char *slash, *dir = strdup(fn); - assert(dir); - - if (!(slash = strrchr(dir, '/'))) - goto finish; - *slash = 0; - - if (pa_make_secure_dir(dir) < 0) - goto finish; - - ret = 0; - -finish: - free(dir); - return ret; -} - -int pa_unix_socket_remove_secure_dir(const char *fn) { - int ret = -1; - char *slash, *dir = strdup(fn); - assert(dir); - - if (!(slash = strrchr(dir, '/'))) - goto finish; - *slash = 0; - - if (rmdir(dir) < 0) - goto finish; - - ret = 0; - -finish: - free(dir); - return ret; -} diff --git a/src/socket-util.h b/src/socket-util.h deleted file mode 100644 index 85133feb..00000000 --- a/src/socket-util.h +++ /dev/null @@ -1,41 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -void pa_socket_peer_to_string(int fd, char *c, size_t l); - -int pa_socket_low_delay(int fd); -int pa_socket_tcp_low_delay(int fd); - -int pa_socket_set_sndbuf(int fd, size_t l); -int pa_socket_set_rcvbuf(int fd, size_t l); - -int pa_unix_socket_is_stale(const char *fn); -int pa_unix_socket_remove_stale(const char *fn); - -int pa_unix_socket_make_secure_dir(const char *fn); -int pa_unix_socket_remove_secure_dir(const char *fn); - -#endif diff --git a/src/source-output.c b/src/source-output.c deleted file mode 100644 index 2705fdb3..00000000 --- a/src/source-output.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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include "source-output.h" - -struct pa_source_output* pa_source_output_new(struct pa_source *s, const char *name, const struct pa_sample_spec *spec) { - struct pa_source_output *o; - struct pa_resampler *resampler = NULL; - int r; - assert(s && spec); - - if (!pa_sample_spec_equal(&s->sample_spec, spec)) - if (!(resampler = pa_resampler_new(&s->sample_spec, spec))) - return NULL; - - o = malloc(sizeof(struct pa_source_output)); - assert(o); - o->name = name ? strdup(name) : NULL; - o->client = NULL; - o->owner = NULL; - o->source = s; - o->sample_spec = *spec; - - o->push = NULL; - o->kill = NULL; - o->userdata = NULL; - o->resampler = resampler; - - assert(s->core); - r = pa_idxset_put(s->core->source_outputs, o, &o->index); - assert(r == 0 && o->index != PA_IDXSET_INVALID); - r = pa_idxset_put(s->outputs, o, NULL); - assert(r == 0); - - return o; -} - -void pa_source_output_free(struct pa_source_output* o) { - assert(o); - - assert(o->source && o->source->core); - pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL); - pa_idxset_remove_by_data(o->source->outputs, o, NULL); - - if (o->resampler) - pa_resampler_free(o->resampler); - - free(o->name); - free(o); -} - -void pa_source_output_kill(struct pa_source_output*i) { - assert(i); - - if (i->kill) - i->kill(i); -} - -void pa_source_output_push(struct pa_source_output *o, const struct pa_memchunk *chunk) { - struct pa_memchunk rchunk; - assert(o && chunk && chunk->length && o->push); - - if (!o->resampler) { - o->push(o, chunk); - return; - } - - pa_resampler_run(o->resampler, chunk, &rchunk); - if (!rchunk.length) - return; - - assert(rchunk.memblock); - o->push(o, &rchunk); - pa_memblock_unref(rchunk.memblock); -} diff --git a/src/source-output.h b/src/source-output.h deleted file mode 100644 index 0e6e2cfd..00000000 --- a/src/source-output.h +++ /dev/null @@ -1,58 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -#include "source.h" -#include "sample.h" -#include "memblockq.h" -#include "resampler.h" -#include "module.h" -#include "client.h" - -struct pa_source_output { - uint32_t index; - - char *name; - struct pa_module *owner; - struct pa_client *client; - struct pa_source *source; - struct pa_sample_spec sample_spec; - - void (*push)(struct pa_source_output *o, const struct pa_memchunk *chunk); - void (*kill)(struct pa_source_output* o); - - struct pa_resampler* resampler; - - void *userdata; -}; - -struct pa_source_output* pa_source_output_new(struct pa_source *s, const char *name, const struct pa_sample_spec *spec); -void pa_source_output_free(struct pa_source_output* o); - -void pa_source_output_kill(struct pa_source_output*o); - -void pa_source_output_push(struct pa_source_output *o, const struct pa_memchunk *chunk); - -#endif diff --git a/src/source.c b/src/source.c deleted file mode 100644 index ccde0e2f..00000000 --- a/src/source.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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include "source.h" -#include "source-output.h" -#include "namereg.h" - -struct pa_source* pa_source_new(struct pa_core *core, const char *name, int fail, const struct pa_sample_spec *spec) { - struct pa_source *s; - char st[256]; - int r; - assert(core && spec && name); - - s = malloc(sizeof(struct pa_source)); - assert(s); - - if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SOURCE, s, fail))) { - free(s); - return NULL; - } - - s->name = strdup(name); - s->description = NULL; - - s->owner = NULL; - s->core = core; - s->sample_spec = *spec; - s->outputs = pa_idxset_new(NULL, NULL); - s->monitor_of = NULL; - - s->notify = NULL; - s->userdata = NULL; - - r = pa_idxset_put(core->sources, s, &s->index); - assert(s->index != PA_IDXSET_INVALID && r >= 0); - - pa_sample_snprint(st, sizeof(st), spec); - fprintf(stderr, "source: created %u \"%s\" with sample spec \"%s\"\n", s->index, s->name, st); - - return s; -} - -void pa_source_free(struct pa_source *s) { - struct pa_source_output *o, *j = NULL; - assert(s); - - pa_namereg_unregister(s->core, s->name); - - while ((o = pa_idxset_first(s->outputs, NULL))) { - assert(o != j); - pa_source_output_kill(o); - j = o; - } - pa_idxset_free(s->outputs, NULL, NULL); - - pa_idxset_remove_by_data(s->core->sources, s, NULL); - - fprintf(stderr, "source: freed %u \"%s\"\n", s->index, s->name); - - free(s->name); - free(s->description); - free(s); -} - -void pa_source_notify(struct pa_source*s) { - assert(s); - - if (s->notify) - s->notify(s); -} - -static int do_post(void *p, uint32_t index, int *del, void*userdata) { - struct pa_memchunk *chunk = userdata; - struct pa_source_output *o = p; - assert(o && o->push && del && chunk); - - pa_source_output_push(o, chunk); - return 0; -} - -void pa_source_post(struct pa_source*s, struct pa_memchunk *chunk) { - assert(s && chunk); - - pa_idxset_foreach(s->outputs, do_post, chunk); -} - -struct pa_source* pa_source_get_default(struct pa_core *c) { - struct pa_source *source; - assert(c); - - if ((source = pa_idxset_get_by_index(c->sources, c->default_source_index))) - return source; - - if (!(source = pa_idxset_first(c->sources, &c->default_source_index))) - return NULL; - - fprintf(stderr, "core: default source vanished, setting to %u.\n", source->index); - return source; -} - -void pa_source_set_owner(struct pa_source *s, struct pa_module *m) { - assert(s); - s->owner = m; -} diff --git a/src/source.h b/src/source.h deleted file mode 100644 index 9c584a6d..00000000 --- a/src/source.h +++ /dev/null @@ -1,61 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -struct pa_source; - -#include -#include "core.h" -#include "sample.h" -#include "idxset.h" -#include "memblock.h" -#include "memchunk.h" -#include "sink.h" - -struct pa_source { - uint32_t index; - - char *name, *description; - struct pa_module *owner; - struct pa_core *core; - struct pa_sample_spec sample_spec; - struct pa_idxset *outputs; - struct pa_sink *monitor_of; - - void (*notify)(struct pa_source*source); - void *userdata; -}; - -struct pa_source* pa_source_new(struct pa_core *core, const char *name, int fail, const struct pa_sample_spec *spec); -void pa_source_free(struct pa_source *s); - -/* Pass a new memory block to all output streams */ -void pa_source_post(struct pa_source*s, struct pa_memchunk *b); - -void pa_source_notify(struct pa_source *s); - -struct pa_source* pa_source_get_default(struct pa_core *c); - -void pa_source_set_owner(struct pa_source *s, struct pa_module *m); - -#endif diff --git a/src/strbuf.c b/src/strbuf.c deleted file mode 100644 index c6a3772d..00000000 --- a/src/strbuf.c +++ /dev/null @@ -1,164 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include - -#include "strbuf.h" - -struct chunk { - struct chunk *next; - size_t length; - char text[]; -}; - -struct pa_strbuf { - size_t length; - struct chunk *head, *tail; -}; - -struct pa_strbuf *pa_strbuf_new(void) { - struct pa_strbuf *sb = malloc(sizeof(struct pa_strbuf)); - assert(sb); - sb->length = 0; - sb->head = sb->tail = NULL; - return sb; -} - -void pa_strbuf_free(struct pa_strbuf *sb) { - assert(sb); - while (sb->head) { - struct chunk *c = sb->head; - sb->head = sb->head->next; - free(c); - } - - free(sb); -} - -char *pa_strbuf_tostring(struct pa_strbuf *sb) { - char *t, *e; - struct chunk *c; - assert(sb); - - t = malloc(sb->length+1); - assert(t); - - e = t; - for (c = sb->head; c; c = c->next) { - memcpy(e, c->text, c->length); - e += c->length; - } - - *e = 0; - - return t; -} - -char *pa_strbuf_tostring_free(struct pa_strbuf *sb) { - char *t; - assert(sb); - t = pa_strbuf_tostring(sb); - pa_strbuf_free(sb); - return t; -} - -void pa_strbuf_puts(struct pa_strbuf *sb, const char *t) { - assert(sb && t); - pa_strbuf_putsn(sb, t, strlen(t)); -} - -void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t l) { - struct chunk *c; - assert(sb && t); - - if (!l) - return; - - c = malloc(sizeof(struct chunk)+l); - assert(c); - - c->next = NULL; - c->length = l; - memcpy(c->text, t, l); - - if (sb->tail) { - assert(sb->head); - sb->tail->next = c; - } else { - assert(!sb->head); - sb->head = c; - } - - sb->tail = c; - sb->length += l; -} - -/* The following is based on an example from the GNU libc documentation */ - -int pa_strbuf_printf(struct pa_strbuf *sb, const char *format, ...) { - int r, size = 100; - struct chunk *c = NULL; - - assert(sb); - - for(;;) { - va_list ap; - - c = realloc(c, sizeof(struct chunk)+size); - assert(c); - - va_start(ap, format); - r = vsnprintf(c->text, size, format, ap); - va_end(ap); - - if (r > -1 && r < size) { - c->length = r; - c->next = NULL; - - if (sb->tail) { - assert(sb->head); - sb->tail->next = c; - } else { - assert(!sb->head); - sb->head = c; - } - - sb->tail = c; - sb->length += r; - - return r; - } - - if (r > -1) /* glibc 2.1 */ - size = r+1; - else /* glibc 2.0 */ - size *= 2; - } -} diff --git a/src/strbuf.h b/src/strbuf.h deleted file mode 100644 index d672c42a..00000000 --- a/src/strbuf.h +++ /dev/null @@ -1,36 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -struct pa_strbuf; - -struct pa_strbuf *pa_strbuf_new(void); -void pa_strbuf_free(struct pa_strbuf *sb); -char *pa_strbuf_tostring(struct pa_strbuf *sb); -char *pa_strbuf_tostring_free(struct pa_strbuf *sb); - -int pa_strbuf_printf(struct pa_strbuf *sb, const char *format, ...) __attribute__ ((format (printf, 2, 3)));; -void pa_strbuf_puts(struct pa_strbuf *sb, const char *t); -void pa_strbuf_putsn(struct pa_strbuf *sb, const char *t, size_t m); - -#endif diff --git a/src/tagstruct.c b/src/tagstruct.c deleted file mode 100644 index 0d93c1e9..00000000 --- a/src/tagstruct.c +++ /dev/null @@ -1,241 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include "tagstruct.h" - -enum tags { - TAG_STRING = 't', - TAG_U32 = 'L', - TAG_S32 = 'l', - TAG_U16 = 'S', - TAG_S16 = 's', - TAG_U8 = 'B', - TAG_S8 = 'b', - TAG_SAMPLE_SPEC = 'a', - TAG_ARBITRARY = 'x' -}; - -struct pa_tagstruct { - uint8_t *data; - size_t length, allocated; - size_t rindex; - - int dynamic; -}; - -struct pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) { - struct pa_tagstruct*t; - - assert(!data || (data && length)); - - t = malloc(sizeof(struct pa_tagstruct)); - assert(t); - t->data = (uint8_t*) data; - t->allocated = t->length = data ? length : 0; - t->rindex = 0; - t->dynamic = !data; - return t; -} - -void pa_tagstruct_free(struct pa_tagstruct*t) { - assert(t); - if (t->dynamic) - free(t->data); - free(t); -} - -uint8_t* pa_tagstruct_free_data(struct pa_tagstruct*t, size_t *l) { - uint8_t *p; - assert(t && t->dynamic && l); - p = t->data; - *l = t->length; - free(t); - return p; -} - -static void extend(struct pa_tagstruct*t, size_t l) { - assert(t && t->dynamic); - - if (l <= t->allocated) - return; - - t->data = realloc(t->data, t->allocated = l+100); - assert(t->data); -} - -void pa_tagstruct_puts(struct pa_tagstruct*t, const char *s) { - size_t l; - assert(t && s); - l = strlen(s)+2; - extend(t, l); - t->data[t->length] = TAG_STRING; - strcpy(t->data+t->length+1, s); - t->length += l; -} - -void pa_tagstruct_putu32(struct pa_tagstruct*t, uint32_t i) { - assert(t); - extend(t, 5); - t->data[t->length] = TAG_U32; - *((uint32_t*) (t->data+t->length+1)) = htonl(i); - t->length += 5; -} - -void pa_tagstruct_putu8(struct pa_tagstruct*t, uint8_t c) { - assert(t); - extend(t, 2); - t->data[t->length] = TAG_U8; - *(t->data+t->length+1) = c; - t->length += 2; -} - -void pa_tagstruct_put_sample_spec(struct pa_tagstruct *t, const struct pa_sample_spec *ss) { - assert(t && ss); - extend(t, 7); - t->data[t->length] = TAG_SAMPLE_SPEC; - t->data[t->length+1] = (uint8_t) ss->format; - t->data[t->length+2] = ss->channels; - *(uint32_t*) (t->data+t->length+3) = htonl(ss->rate); - t->length += 7; -} - - -void pa_tagstruct_put_arbitrary(struct pa_tagstruct *t, const void *p, size_t length) { - assert(t && p); - - extend(t, 5+length); - t->data[t->length] = TAG_ARBITRARY; - *((uint32_t*) (t->data+t->length+1)) = htonl(length); - if (length) - memcpy(t->data+t->length+5, p, length); - t->length += 5+length; -} - -int pa_tagstruct_gets(struct pa_tagstruct*t, const char **s) { - int error = 0; - size_t n; - char *c; - assert(t && s); - - if (t->rindex+2 > t->length) - return -1; - - if (t->data[t->rindex] != TAG_STRING) - return -1; - - error = 1; - for (n = 0, c = (char*) (t->data+t->rindex+1); t->rindex+1+n < t->length; n++, c++) - if (!*c) { - error = 0; - break; - } - - if (error) - return -1; - - *s = (char*) (t->data+t->rindex+1); - - t->rindex += n+2; - return 0; -} - -int pa_tagstruct_getu32(struct pa_tagstruct*t, uint32_t *i) { - assert(t && i); - - if (t->rindex+5 > t->length) - return -1; - - if (t->data[t->rindex] != TAG_U32) - return -1; - - *i = ntohl(*((uint32_t*) (t->data+t->rindex+1))); - t->rindex += 5; - return 0; -} - -int pa_tagstruct_getu8(struct pa_tagstruct*t, uint8_t *c) { - assert(t && c); - - if (t->rindex+2 > t->length) - return -1; - - if (t->data[t->rindex] != TAG_U8) - return -1; - - *c = t->data[t->rindex+1]; - t->rindex +=2; - return 0; -} - -int pa_tagstruct_get_sample_spec(struct pa_tagstruct *t, struct pa_sample_spec *ss) { - assert(t && ss); - - if (t->rindex+7 > t->length) - return -1; - - if (t->data[t->rindex] != TAG_SAMPLE_SPEC) - return -1; - - ss->format = t->data[t->rindex+1]; - ss->channels = t->data[t->rindex+2]; - ss->rate = ntohl(*(uint32_t*) (t->data+t->rindex+3)); - - t->rindex += 7; - return 0; -} - -int pa_tagstruct_get_arbitrary(struct pa_tagstruct *t, const void **p, size_t length) { - assert(t && p); - - if (t->rindex+5+length > t->length) - return -1; - - if (t->data[t->rindex] != TAG_ARBITRARY) - return -1; - - if (ntohl(*((uint32_t*) (t->data+t->rindex+1))) != length) - return -1; - - *p = t->data+t->rindex+5; - t->rindex += 5+length; - return 0; -} - -int pa_tagstruct_eof(struct pa_tagstruct*t) { - assert(t); - return t->rindex >= t->length; -} - -const uint8_t* pa_tagstruct_data(struct pa_tagstruct*t, size_t *l) { - assert(t && t->dynamic && l); - *l = t->length; - return t->data; -} - diff --git a/src/tagstruct.h b/src/tagstruct.h deleted file mode 100644 index aefd03c4..00000000 --- a/src/tagstruct.h +++ /dev/null @@ -1,53 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include - -#include "sample.h" - -struct pa_tagstruct; - -struct pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length); -void pa_tagstruct_free(struct pa_tagstruct*t); -uint8_t* pa_tagstruct_free_data(struct pa_tagstruct*t, size_t *l); - -void pa_tagstruct_puts(struct pa_tagstruct*t, const char *s); -void pa_tagstruct_putu32(struct pa_tagstruct*t, uint32_t i); -void pa_tagstruct_putu8(struct pa_tagstruct*t, uint8_t c); -void pa_tagstruct_put_sample_spec(struct pa_tagstruct *t, const struct pa_sample_spec *ss); -void pa_tagstruct_put_arbitrary(struct pa_tagstruct*t, const void *p, size_t length); - -int pa_tagstruct_gets(struct pa_tagstruct*t, const char **s); -int pa_tagstruct_getu32(struct pa_tagstruct*t, uint32_t *i); -int pa_tagstruct_getu8(struct pa_tagstruct*t, uint8_t *c); -int pa_tagstruct_get_sample_spec(struct pa_tagstruct *t, struct pa_sample_spec *ss); -int pa_tagstruct_get_arbitrary(struct pa_tagstruct *t, const void **p, size_t length); - -int pa_tagstruct_eof(struct pa_tagstruct*t); -const uint8_t* pa_tagstruct_data(struct pa_tagstruct*t, size_t *l); - - - -#endif diff --git a/src/tokenizer.c b/src/tokenizer.c deleted file mode 100644 index c7f18d26..00000000 --- a/src/tokenizer.c +++ /dev/null @@ -1,89 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include "tokenizer.h" -#include "dynarray.h" - -struct pa_tokenizer { - struct pa_dynarray *dynarray; -}; - -static void token_free(void *p, void *userdata) { - free(p); -} - -static void parse(struct pa_dynarray*a, const char *s, unsigned args) { - int infty = 0; - const char delimiter[] = " \t\n\r"; - const char *p; - assert(a && s); - - if (args == 0) - infty = 1; - - p = s+strspn(s, delimiter); - while (*p && (infty || args >= 2)) { - size_t l = strcspn(p, delimiter); - char *n = strndup(p, l); - assert(n); - pa_dynarray_append(a, n); - p += l; - p += strspn(p, delimiter); - args--; - } - - if (args && *p) { - char *n = strdup(p); - assert(n); - pa_dynarray_append(a, n); - } -} - -struct pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args) { - struct pa_tokenizer *t; - - t = malloc(sizeof(struct pa_tokenizer)); - assert(t); - t->dynarray = pa_dynarray_new(); - assert(t->dynarray); - - parse(t->dynarray, s, args); - return t; -} - -void pa_tokenizer_free(struct pa_tokenizer *t) { - assert(t); - pa_dynarray_free(t->dynarray, token_free, NULL); - free(t); -} - -const char *pa_tokenizer_get(struct pa_tokenizer *t, unsigned i) { - assert(t); - return pa_dynarray_get(t->dynarray, i); -} diff --git a/src/tokenizer.h b/src/tokenizer.h deleted file mode 100644 index 7d1cf9b5..00000000 --- a/src/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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -struct pa_tokenizer; - -struct pa_tokenizer* pa_tokenizer_new(const char *s, unsigned args); -void pa_tokenizer_free(struct pa_tokenizer *t); - -const char *pa_tokenizer_get(struct pa_tokenizer *t, unsigned i); - -#endif diff --git a/src/util.c b/src/util.c deleted file mode 100644 index 6e75c240..00000000 --- a/src/util.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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "util.h" - -void pa_make_nonblock_fd(int fd) { - int v; - - if ((v = fcntl(fd, F_GETFL)) >= 0) - if (!(v & O_NONBLOCK)) - fcntl(fd, F_SETFL, v|O_NONBLOCK); -} - -int pa_make_secure_dir(const char* dir) { - struct stat st; - - if (mkdir(dir, 0700) < 0) - if (errno != EEXIST) - return -1; - - if (lstat(dir, &st) < 0) - goto fail; - - if (!S_ISDIR(st.st_mode) || (st.st_uid != getuid()) || ((st.st_mode & 0777) != 0700)) - goto fail; - - return 0; - -fail: - rmdir(dir); - return -1; -} - -ssize_t pa_loop_read(int fd, void*data, size_t size) { - ssize_t ret = 0; - assert(fd >= 0 && data && size); - - while (size > 0) { - ssize_t r; - - if ((r = read(fd, data, size)) < 0) - return r; - - if (r == 0) - break; - - ret += r; - data += r; - size -= r; - } - - return ret; -} - -ssize_t pa_loop_write(int fd, const void*data, size_t size) { - ssize_t ret = 0; - assert(fd >= 0 && data && size); - - while (size > 0) { - ssize_t r; - - if ((r = write(fd, data, size)) < 0) - return r; - - if (r == 0) - break; - - ret += r; - data += r; - size -= r; - } - - return ret; -} - -void pa_check_for_sigpipe(void) { - struct sigaction sa; - - if (sigaction(SIGPIPE, NULL, &sa) < 0) { - fprintf(stderr, __FILE__": sigaction() failed: %s\n", strerror(errno)); - return; - } - - if (sa.sa_handler == SIG_DFL) - fprintf(stderr, "polypaudio: WARNING: SIGPIPE is not trapped. This might cause malfunction!\n"); -} - -/* The following is based on an example from the GNU libc documentation */ -char *pa_sprintf_malloc(const char *format, ...) { - int size = 100; - char *c = NULL; - - assert(format); - - for(;;) { - int r; - va_list ap; - - c = realloc(c, size); - assert(c); - - va_start(ap, format); - r = vsnprintf(c, size, format, ap); - va_end(ap); - - if (r > -1 && r < size) - return c; - - if (r > -1) /* glibc 2.1 */ - size = r+1; - else /* glibc 2.0 */ - size *= 2; - } -} diff --git a/src/util.h b/src/util.h deleted file mode 100644 index 96fde11c..00000000 --- a/src/util.h +++ /dev/null @@ -1,38 +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 General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - polypaudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with polypaudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -void pa_make_nonblock_fd(int fd); - -int pa_make_secure_dir(const char* dir); - -ssize_t pa_loop_read(int fd, void*data, size_t size); -ssize_t pa_loop_write(int fd, const void*data, size_t size); - -void pa_check_for_sigpipe(void); - -char *pa_sprintf_malloc(const char *format, ...) __attribute__ ((format (printf, 1, 2))); - -#endif -- cgit